Les flux dans Node.js sont très puissants. Ils prennent en charge le traitement de fichiers potentiellement volumineux ainsi que le traitement et la livraison de données abstraites dans certains scénarios. Parce qu'il est si facile à utiliser, dans les combats réels, nous écrivons souvent des fonctions/bibliothèques d'outils basées sur celui-ci. Cependant, souvent en raison de notre propre négligence de certaines caractéristiques des flux, les fonctions/bibliothèques écrites ne répondront pas aux exigences dans certains cas. . effet souhaité, ou poser des mines cachées. Cet article fournira deux conseils qui me semblent utiles lors de l’écriture d’outils basés sur les flux.
1. Méfiez-vous des fuites de mémoire EVENTEMITTER
Dans une fonction qui peut être appelée plusieurs fois, si vous devez ajouter un écouteur d'événement au flux pour effectuer certaines opérations. Ensuite, vous devez vous méfier des fuites de mémoire provoquées par l'ajout d'écouteurs :
'use strict'; const fs = require('fs'); const co = require('co'); function getSomeDataFromStream (stream) { let data = stream.read(); if (data) return Promise.resolve(data); if (!stream.readable) return Promise.resolve(null); return new Promise((resolve, reject) => { stream.once('readable', () => resolve(stream.read())); stream.on('error', reject); stream.on('end', resolve); }) } let stream = fs.createReadStream('/Path/to/a/big/file'); co(function *() { let chunk; while ((chunk = yield getSomeDataFromStream(stream)) !== null) { console.log(chunk); } }).catch(console.error);
Dans le code ci-dessus, la fonction getSomeDataFromStream terminera la promesse lorsque le flux signale une erreur ou n'a aucune donnée en écoutant l'événement d'erreur et l'événement de fin. Cependant, lors de l'exécution du code, nous verrons bientôt le message d'alarme dans la console : (nœud) avertissement : possible fuite de mémoire EventEmitter détectée. 11 écouteurs d'erreur ajoutés. Utilisez submitter.setMaxListeners() pour augmenter la limite., car nous sommes dans chaque. Chaque fois que cette fonction est appelée, un écouteur d'événement d'erreur et un écouteur d'événement de fin supplémentaires sont ajoutés au flux entrant. Afin d'éviter cette fuite de mémoire potentielle, nous devons nous assurer qu'après chaque exécution de fonction, tous les écouteurs supplémentaires ajoutés par cet appel sont effacés pour garder la fonction sans pollution :
function getSomeDataFromStream (stream) { let data = stream.read(); if (data) return Promise.resolve(data); if (!stream.readable) return Promise.resolve(null); return new Promise((resolve, reject) => { stream.once('readable', onData); stream.on('error', onError); stream.on('end', done); function onData () { done(); resolve(stream.read()); } function onError (err) { done(); reject(err); } function done () { stream.removeListener('readable', onData); stream.removeListener('error', onError); stream.removeListener('end', done); } }) }
Deuxièmement, assurez-vous que le rappel de la fonction outil est appelé après le traitement des données
Les fonctions d'outils fournissent souvent un paramètre de fonction de rappel au monde extérieur. Une fois toutes les données du flux traitées, elles sont déclenchées avec une valeur spécifiée. L'approche habituelle consiste à suspendre la fonction de rappel dans l'événement de fin du flux. , mais si elle est traitée, les fonctions sont des opérations asynchrones qui prennent du temps et les fonctions de rappel peuvent être appelées avant que toutes les données ne soient traitées :
'use strict'; const fs = require('fs'); let stream = fs.createReadStream('/Path/to/a/big/file'); function processSomeData (stream, callback) { stream.on('data', (data) => { // 对数据进行一些异步耗时操作 setTimeout(() => console.log(data), 2000); }); stream.on('end', () => { // ... callback() }) } processSomeData(stream, () => console.log('end'));
Le rappel de code ci-dessus peut être appelé lorsque toutes les données n'ont pas été traitées, car l'événement de fin du flux n'est déclenché que lorsque les données du flux sont lues. Nous devons donc vérifier en plus si les données ont été traitées :
function processSomeData (stream, callback) { let count = 0; let finished = 0; let isEnd = false; stream.on('data', (data) => { count++; // 对数据进行一些异步耗时操作 setTimeout(() => { console.log(data); finished++; check(); }, 2000); }); stream.on('end', () => { isEnd = true; // ... check(); }) function check () { if (count === finished && isEnd) callback() } }
De cette façon, le rappel sera déclenché une fois que toutes les données auront été traitées.