web-dev-qa-db-ja.com

node.jsで読み取り可能なストリームとして配列値を出力/パイプする方法は?

配列から読み取り可能なストリームを作成し、値を書き込み可能なストリームにパイプするための最良の方法は何ですか?サブスタックの setIntervalを使用しているのを見てきましたが、間隔値に0を使用して正常に実装できますが、大量のデータを繰り返し処理しており、毎回gcをトリガーすると速度が低下します。

// Working with the setInterval wrapper
var arr = [1, 5, 3, 6, 8, 9];

function createStream () {
    var t = new stream;
    t.readable = true;
    var times = 0;
    var iv = setInterval(function () {
        t.emit('data', arr[times]);
        if (++times === arr.length) {
            t.emit('end');
            clearInterval(iv);
        }
    }
}, 0);

// Create the writable stream s
// ....

createStream().pipe(s);

私がやりたいのは、setIntervalなしで値を出力することです。おそらく、次のような非同期モジュールを使用します。

async.forEachSeries(arr, function(item, cb) {
    t.emit('data', item);
    cb();
}, function(err) {
 if (err) {
     console.log(err);
 }
 t.emit('end');
});

この場合、配列を繰り返してデータを出力しますが、値をパイプすることはありません。私はすでにshinoutの ArrayStream を見てきましたが、それはv0.10より前に作成されたものであり、私が探しているよりも少しオーバーヘッドが大きいと思います。

12
TankofVines

これには ArrayStream を使用することになりました。 GCが頻繁にトリガーされるという問題は解決しました。ノードから再帰的なprocess.nextTickの警告が表示されていたため、ArrayStreamのnextTickコールバックをsetImmediateに変更しました。これにより、警告が修正され、正常に機能しているようです。

2
TankofVines

この問題は、読み取り可能なストリームを作成して値をプッシュすることで解決できます。

ストリームは苦痛ですが、ライブラリを使用するよりも 簡単 から 直接操作する の方が多いことがよくあります。

ストリーミングする文字列またはバッファの配列

文字列またはバッファの配列を使用している場合、これは機能します。

_'use strict'
const Stream = require('stream')
const readable = new Stream.Readable()

readable.pipe(process.stdout)

const items = ['a', 'b', 'c']
items.forEach(item => readable.Push(item))

// no more data
readable.Push(null)
_

ノート:

  • readable.pipe(process.stdout)は、ストリームを「フロー」モードにすることと、readableからデータを受信するようにprocess.stdout書き込み可能ストリームを設定することの2つのことを行います。
  • _Readable#Push_メソッドは、読み取り可能なストリームの作成者向けであり、ストリームの利用者向けではありません。
  • これ以上データがないことを通知するには、Readable#Push(null)を実行する必要があります。

ストリーミングする非文字列の配列

文字列でもバッファでもないものの配列からストリームを作成するには、読み取り可能なストリームと書き込み可能なストリームの両方が "オブジェクトモード" である必要があります。以下の例では、次の変更を加えました。

  • 読み取り可能なストリームを_{objectMode: true}_で初期化します
  • _process.stdout_にパイプする代わりに、オブジェクトモードの単純な書き込み可能なストリームにパイプします。

    _  'use strict'
      const Stream = require('stream')
    
      const readable = new Stream.Readable({objectMode: true})
    
      const writable = new Stream.Writable({objectMode: true})
      writable._write = (object, encoding, done) => {
        console.log(object)
    
        // ready to process the next chunk
        done()
      }
    
      readable.pipe(writable)
    
      const items = [1, 2, 3]
      items.forEach(item => readable.Push(item))
    
      // end the stream
      readable.Push(null)
    _

パフォーマンスノート

データはどこから来ていますか?ストリーミングデータソースの場合は、配列との間で変換するよりも、変換ストリームを使用してストリームを操作する方が適切です。

19
Max Heiber

tl; dr;

const items = [1,2,3]
const stream = new Readable({
  objectMode: true,
  read() {
    const item = items.pop()
    if (!item) {
      this.Push(null)
    }
    this.Push(item)
  },
})
8
Lajos

これは古い質問ですが、誰かがこれに遭遇した場合、 node-stream-array はNode.jsのはるかに単純でエレガントな実装です> = v0.10

var streamify = require('stream-array'),
  os = require('os');

streamify(['1', '2', '3', os.EOL]).pipe(process.stdout);
2

Node 12.3以降、代わりに stream.Readable.from(iterable, [options]) を使用できます。

const { Readable } = require('stream');
const readableStream = Readable.from(arr);
1
austince