Node.js のストリームは非常に強力で、一部のシナリオでは、潜在的に大きなファイルの処理と抽象データの処理と配信をサポートします。非常に使いやすいため、実際の戦闘ではこれをベースにしてツールの関数/ライブラリを作成することがよくありますが、ストリームの特性を無視しているため、作成した関数/ライブラリが要件を満たさない場合があります。 . 望ましい効果、またはいくつかの隠された地雷を敷設します。この記事では、ストリームベースのツールを作成するときに役立つと思われる 2 つのヒントを紹介します。
1. EVENTEMITTER のメモリ リークに注意してください
複数回呼び出される可能性のある関数内で、特定の操作を実行するためにストリームにイベント リスナーを追加する必要がある場合。次に、リスナーの追加によって引き起こされるメモリ リークに注意する必要があります:
'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);
上記のコードでは、ストリームがエラーを報告するかデータがない場合、getSomeDataFromStream 関数はエラー イベントと終了イベントをリッスンすることで Promise を完了します。ただし、コードを実行すると、すぐにコンソールにアラーム メッセージが表示されます: (ノード) 警告: EventEmitter メモリ リークの可能性が検出されました。制限を増やすには、emitter.setMaxListeners() を使用してください。この関数が呼び出されるたびに、追加のエラー イベント リスナーと終了イベント リスナーが受信ストリームに追加されます。この潜在的なメモリ リークを回避するには、関数を実行するたびに、この呼び出しによって追加されたすべてのリスナーがクリアされて関数が汚染されないようにする必要があります。
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); } }) }
2 番目に、データの処理後にツール関数のコールバックが呼び出されることを確認します
ツール関数は、ストリーム内のすべてのデータが処理された後、コールバック関数パラメーターを外部に提供することがよくあります。通常のアプローチは、ストリームの終了イベントでコールバック関数をハングすることです。ただし、処理される場合 関数は時間のかかる非同期操作であり、すべてのデータが処理される前にコールバック関数が呼び出される場合があります:
'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'));
ストリームの終了イベントはストリーム内のデータが読み取られたときにのみトリガーされるため、すべてのデータが処理されていないときに上記のコードのコールバックが呼び出される可能性があります。したがって、データが処理されたかどうかをさらに確認する必要があります:
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() } }
このようにして、すべてのデータが処理された後にコールバックがトリガーされます。