Concatenate two (or n) streams

前端 未结 10 1231
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-24 02:34
  • 2 streams:

    Given readable streams stream1 and stream2, what\'s an idiomatic (concise) way to get a stream containing

10条回答
  •  生来不讨喜
    2020-12-24 02:52

    You might be able to make it more concise, but here's one that works:

    var util = require('util');
    var EventEmitter = require('events').EventEmitter;
    
    function ConcatStream(streamStream) {
      EventEmitter.call(this);
      var isStreaming = false,
        streamsEnded = false,
        that = this;
    
      var streams = [];
      streamStream.on('stream', function(stream){
        stream.pause();
        streams.push(stream);
        ensureState();
      });
    
      streamStream.on('end', function() {
        streamsEnded = true;
        ensureState();
      });
    
      var ensureState = function() {
        if(isStreaming) return;
        if(streams.length == 0) {
          if(streamsEnded)
            that.emit('end');
          return;
        }
        isStreaming = true;
        streams[0].on('data', onData);
        streams[0].on('end', onEnd);
        streams[0].resume();
      };
    
      var onData = function(data) {
        that.emit('data', data);
      };
    
      var onEnd = function() {
        isStreaming = false;
        streams[0].removeAllListeners('data');
        streams[0].removeAllListeners('end');
        streams.shift();
        ensureState();
      };
    }
    
    util.inherits(ConcatStream, EventEmitter);
    

    We keep track of state with streams (the queue of streams;push to the back and shift from the front), isStreaming, and streamsEnded. When we get a new stream, we push it, and when a stream ends, we stop listening and shift it. When the stream of streams ends, we set streamsEnded.

    On each of these events, we check the state we're in. If we're already streaming (piping a stream), we do nothing. If the queue is empty and streamsEnded is set, we emit the end event. If there is something in the queue, we resume it and listen to its events.

    *Note that pause and resume are advisory, so some streams may not behave correctly, and would require buffering. This exercise is left to the reader.

    Having done all of this, I would do the n=2 case by constructing an EventEmitter, creating a ConcatStream with it, and emitting two stream events followed by an end event. I'm sure it could be done more concisely, but we may as well use what we've got.

提交回复
热议问题