Flux
Introduction
Ce chapitre présente le concept de flux. C’est un outil majeur de Node, et c’est pour cela que le sujet est traité à part entière dans cette partie. Premièrement, c’est la définition précise de ce qu’est un flux qui sera traitée, ainsi que les raisons de l’utilisation de ce paradigme dans vos projets.
Ensuite sera abordée la construction d’un flux, puis son utilisation. Suivront des exemples concrets d’utilisation pour des traitements aussi bien « réseau » que « fichier ».
En enfin, quelques détails seront donnés pour les utiliser sans crainte d’incompatibilité entre différentes version de Node.
1. Qu’est-ce qu’un flux ?
Si vous connaissez le fonctionnement des flux Unix, cette partie vous semblera triviale : le fonctionnement est en effet tout proche des fameux pipes. Si ce n’est pas le cas, imaginez un flux comme un travail à la chaîne sur un tapis roulant : il peut y avoir différents types d’étapes, comme des contrôles (lecture d’un code-barre par exemple), un ajout (poser un produit), ou encore des modifications (appliquer de la peinture). Le flux est réellement dynamique, il s’écoule.
Ce paradigme permet de passer simplement des données tout en autorisant des combinaisons puissantes et flexibles...
Construction
1. Readable
Pour créer un flux de lecture, il suffit d’instancier un objet de type Readable et d’implémenter la méthode interne _read() qui a pour rôle d’émettre les messages. Cette méthode est appelée automatiquement par Node quand ce flux est connecté à un flux d’écriture via pipe() (voir la section Utilisation).
L’exemple ci-après illustre la création d’un flux de lecture :
var stream = new Readable({
objectMode: true
});
var array = ['foo', 'bar', 'baz'];
var i = 0;
var n = array.length;
stream._read = function (size) {
// Tant qu'il reste des entrées dans le tableau,
// on tente de les envoyer.
while (i < n) {
// Si push renvoie false, c'est que le consommateur n'a pas
// fini de traiter les messages envoyés.
//
// Il n'est donc pas nécessaire de continuer pour le moment
// pour éviter de saturer la mémoire (contre-pression).
if (!this.push(array[i++])) {
return;
}
}
// Signale la fin du flux.
this.push(null);
};
On peut constater deux choses. Tout d’abord, on instancie un flux de type objet, en passant l’option objectMode: true. Ensuite, la fonction reçoit un paramètre size qui n’est pas utilisé ici. C’est un entier qui indique...
Utilisation
1. Lecture
Dans les exemples de cette section, nous allons utiliser la variable stream pour désigner un flux de lecture quelconque, correspondant par exemple à la lecture d’un fichier ou bien à l’entrée standard du programme.
Lecture d’un fichier :
var stream = require('fs').createReadStream('movie.mkv');
Entrée standard du programme :
var stream = process.stdin;
a. Mode flot
Le mode le plus simple d’utilisation d’un flux de lecture est le mode flot : les données arrivent au fur et à mesure de leur disponibilité avec des événements data jusqu’à la fin du flux matérialisé par un événement end.
Ce cas est illustré dans l’exemple ci-après : tant que le flux est en cours de lecture, il est affiché « donnée reçue » à l’écran. Et à la fin du flux, c’est un autre message qui est visible : « le flux s’est terminé ».
stream.on('data', function (data) {
console.log('donnée reçue', data);
});
stream.on('end', function () {
console.log('le flux s\'est terminé');
});
C’est également le mode le plus...
Omniprésence dans Node
Les flux sont vraiment partout dans Node ; ils sont utilisés dans un nombre incalculable de modules différents. Voici quelques exemples d’utilisation.
1. Réseau
Très adaptés aux problématiques de réseaux, les flux permettent d’aborder des cas complexes en toute simplicité. L’exemple suivant l’illustre parfaitement, puisque l’on peut rediriger n’importe où et facilement le résultat de get() depuis n’importe quelle URL.
var httpGet = require('http').get;
get('http://example.com', function (response) {
// response est un flux qui peut être redirigé sur la sortie
// standard ou vers un fichier par exemple.
response.pipe(process.stdin);
});
2. Fichiers
Les flux peuvent aussi être utilisés pour faire de la copie de fichiers, avec tous les avantages qu’ils procurent (contre-pression notamment). D’abord, un flux d’écriture est créé sur un fichier source, puis un flux de lecture sur un fichier de destination. La méthode pipe() permet de copier le contenu du premier dans le deuxième.
fs.createReadStream('foo.txt')
.pipe(fs.createWriteStream('bar.txt'))
;
Introduisons...
Implémentations et compatibilité
L’implémentation des flux a évolué au fur et à mesure des versions de Node. La première, la version 1, a duré jusqu’à Node 0.8. Ensuite, c’est la deuxième, la version 2, qui a apporté entre autres la gestion de la contre-pression (à partir de Node 0.10 ceci dit). Enfin, la version 3 permet une meilleure rétrocompatibilité avec la première implémentation des flux, dès la version 0.12 de Node.
1. Support des flux version 1
Les versions 2 et 3 des flux permettent très facilement de supporter des flux de lecture de la version 1. Par exemple, avec un flux de lecture qui utilise la version 1, il suffit de créer un nouveau flux de même type (donc Readable aussi), puis de lui passer l’ancien via stream.wrap. Ce support n’est nécessaire que pour des flux de lecture, qui ont beaucoup évolué depuis la première implémentation, contrairement aux autres qui sont restés bien plus stables.
var stream = new Readable();
stream.wrap(oldStream);
2. S’affranchir de la version de Node
Il est de bonne pratique de ne pas s’appuyer directement sur l’implémentation des flux du module standard stream de Node mais d’utiliser l’implémentation externe disponible dans le paquet readable-stream. Vous trouverez...