UNPKG

stream-flow-control

Version:
142 lines (127 loc) 4.74 kB
const {Writable, Readable} = require('stream'); const {Manager} = require('../manager/manager.js'); /** * Stream every element of an array individually. * * @extends Writable */ class FlowEach extends Writable { /** * Create a FlowEach stream * @param {object} [options] Global options * @param {string} [options.name] Name for this stream */ constructor(options) { options = {...options, objectMode: true}; super(options); this.options = options; this.type = 'FlowEach'; if(this.options.name) Manager.set(this.type, this); this._reader = null; this._corking_readers = {}; this._reader_messages = []; this._none_reader = null; } /** * Set stream(s) to pipe to * @param {(Writable|Writable[])} dst * @returns {(Writable|FlowAll)} if dst is an array, then returns "this", else dst is returned */ pipe(dst) { if(!this._reader) { const self = this; this._reader = new Readable({ objectMode: true, read() { if(self._writableState.corked && self._corking_readers['reader']) { self.uncork(); delete self._corking_readers['reader']; self._deliver_messages(); } } }); this._reader.options = {...this._reader.options||{}, name: this.options.name}; this._reader._readableState.sync = false; } const pipes = Array.isArray(dst)?dst:[dst]; pipes.forEach(dst=>this._reader.pipe(dst)); return Array.isArray(dst)?this:dst; } /** * Set stream(s) to pipe to when no other stream was piped * @param {(Writable|Writable[])} dst Destination stream * @returns {(Writable|FlowAll)} if dst is an array, then returns "this", else dst is returned */ none(dst) { if(!this._none_reader) { const self = this; this._none_reader = new Readable({ objectMode: true, read() { if(self._writableState.corked && self._corking_readers['reader']) { self.uncork(); delete self._corking_readers['reader']; } } }); this._none_reader.options = {...this._none_reader.options||{}, name: this.options.name, port: 'none'}; this._none_reader._readableState.sync = false; } const pipes = Array.isArray(dst)?dst:[dst]; pipes.forEach(dst=>this._none_reader.pipe(dst)); return Array.isArray(dst)?this:dst; } _deliver_messages() { if(this._reader) { while(this._reader_messages.length) { if(!this._reader.push(this._reader_messages.shift()) && !this._corking_readers['reader']) { this.cork(); this._corking_readers['reader'] = true; break; } } } if(!this._reader_messages.length && this._needsEnd) { this._reader && this._reader.push(null); this._none_reader && this._none_reader.push(null); } } _write(payload, encoding, cb) { let data; if(this.goal) { // Is goal player data = payload.data; } else { data = payload; } if(this._reader) { this._reader_messages = this._reader_messages.concat((!Array.isArray(data)?[data]:data)); if(this.goal) { this._reader_messages = this._reader_messages.map(data=>payload.getChild(this).setData(data)); }; this._deliver_messages(); } if(!this._reader && this._none_reader) { if(this.options.none_reason) { let data = typeof this.options.none_reason == 'function'?this.options.none_reason(payload):this.options.none_reason; payload = (this.goal)?payload.getChild(this).setData(data):data; } else { payload = this.goal?payload.getChild(this):payload } if(!this._none_reader.push(payload) && !this._corking_readers['none']) { this.cork(); this._corking_readers['none'] = true; } } cb(); } end() { if(!this._reader_messages.length) { this._reader && this._reader.push(null); this._none_reader && this._none_reader.push(null); } else { this._needsEnd = true; } } } module.exports.FlowEach = FlowEach;