Nodeを使うjsの中のストリーム書き込みツールの時の2点の小さいtips

3652 ワード

Node.jsのストリームは非常に強力で、潜在的な大きなファイルの処理をサポートし、いくつかのシーンでのデータの処理と伝達を抽象化しています.このように使いやすいからこそ、実戦ではツール関数/ライブラリを書くことが多いが、自分の対流のいくつかの特性の不注意によって、書かれた関数/ライブラリが所望の効果に達しない場合や、隠れた地雷を埋める場合がある.本稿では,ストリームベースのツールを記述する際に,いくつかの役に立つと思う2つのtipsを提供する.

一、EventEmitterメモリの漏洩を警戒する


複数回呼び出される可能性のある関数では、ストリームにイベントリスナーを追加していくつかの操作を実行する必要がある場合.では、Listenerの追加によるメモリの漏洩に注意する必要があります.
'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関数は、errorイベントおよびendイベントをリスニングすることによって、ストリームエラーまたはデータがない場合に、このPromiseを完了する.しかし、コードを実行するたびに、関数を呼び出すたびに、追加の(node) warning: possible EventEmitter memory leak detected. 11 error listeners added. Use emitter.setMaxListeners() to increase limit.イベントリスナーとerrorイベントリスナーが追加されるため、コンソールにアラーム情報が表示されます.このような潜在的なメモリ漏洩を回避するために、関数の実行が完了するたびに、今回の呼び出しで追加された追加のリスナーをすべて消去し、関数を汚染しないようにします.
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);
    }
  })
}

二、保証ツール関数のコールバックはデータを処理した後に呼び出される


ツール関数は、ストリーム内のすべてのデータを処理した後、指定された値を持ってトリガーされるコールバック関数パラメータを外部に提供することがよくあります.通常、コールバック関数の呼び出しをストリームのendイベントに掛けますが、処理関数が時間のかかる非同期操作である場合、コールバック関数の数は、すべてのデータが処理される前に呼び出される可能性があります.
'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'));

以上のコードendコールバックは、ストリームのcallbackイベントのトリガタイミングがストリーム中のデータが読み終わったときだけであるため、データがすべて処理されていないときに呼び出される可能性がある.データが処理されたかどうかをさらに確認する必要があります.
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()
  }
}

これにより、コールバックはすべてのデータが処理された後にトリガーされます.