|
|
@ -741,8 +741,8 @@ the [`'drain'`][] event before writing more data. |
|
|
|
|
|
|
|
To implement any sort of stream, the pattern is the same: |
|
|
|
|
|
|
|
1. Extend the appropriate parent class in your own subclass. (The |
|
|
|
[`util.inherits()`][] method is particularly helpful for this.) |
|
|
|
1. Extend the appropriate parent class in your own subclass via the `extends` |
|
|
|
keyword. |
|
|
|
2. Call the appropriate parent class constructor in your constructor, |
|
|
|
to be sure that the internal mechanisms are set up properly. |
|
|
|
3. Implement one or more specific methods, as detailed below. |
|
|
@ -945,10 +945,10 @@ could wrap the low-level source object by doing something like this: |
|
|
|
// and an `ondata` member that gets called when it has data, and |
|
|
|
// an `onend` member that gets called when the data is over. |
|
|
|
|
|
|
|
util.inherits(SourceWrapper, Readable); |
|
|
|
|
|
|
|
function SourceWrapper(options) { |
|
|
|
Readable.call(this, options); |
|
|
|
class SourceWrapper extends Readable { |
|
|
|
constructor(options) { |
|
|
|
super(options); |
|
|
|
|
|
|
|
this._source = getLowlevelSourceObject(); |
|
|
|
|
|
|
@ -963,13 +963,13 @@ function SourceWrapper(options) { |
|
|
|
this._source.onend = () => { |
|
|
|
this.push(null); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
// _read will be called when the stream wants to pull more data in |
|
|
|
// the advisory size argument is ignored in this case. |
|
|
|
SourceWrapper.prototype._read = function(size) { |
|
|
|
} |
|
|
|
// _read will be called when the stream wants to pull more data in |
|
|
|
// the advisory size argument is ignored in this case. |
|
|
|
_read(size) { |
|
|
|
this._source.readStart(); |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
#### Example: A Counting Stream |
|
|
@ -981,16 +981,15 @@ from 1 to 1,000,000 in ascending order, and then ends. |
|
|
|
|
|
|
|
```js |
|
|
|
const Readable = require('stream').Readable; |
|
|
|
const util = require('util'); |
|
|
|
util.inherits(Counter, Readable); |
|
|
|
|
|
|
|
function Counter(opt) { |
|
|
|
Readable.call(this, opt); |
|
|
|
class Counter extends Readable { |
|
|
|
constructor(opt) { |
|
|
|
super(opt); |
|
|
|
this._max = 1000000; |
|
|
|
this._index = 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Counter.prototype._read = function() { |
|
|
|
_read() { |
|
|
|
var i = this._index++; |
|
|
|
if (i > this._max) |
|
|
|
this.push(null); |
|
|
@ -999,7 +998,8 @@ Counter.prototype._read = function() { |
|
|
|
var buf = Buffer.from(str, 'ascii'); |
|
|
|
this.push(buf); |
|
|
|
} |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
#### Example: SimpleProtocol v1 (Sub-optimal) |
|
|
@ -1022,15 +1022,11 @@ However, this would be better implemented as a [Transform][] stream. See |
|
|
|
// alternative example below under the Transform section. |
|
|
|
|
|
|
|
const Readable = require('stream').Readable; |
|
|
|
const util = require('util'); |
|
|
|
|
|
|
|
util.inherits(SimpleProtocol, Readable); |
|
|
|
|
|
|
|
function SimpleProtocol(source, options) { |
|
|
|
if (!(this instanceof SimpleProtocol)) |
|
|
|
return new SimpleProtocol(source, options); |
|
|
|
class SimpleProtocol extends Readable { |
|
|
|
constructor(source, options) { |
|
|
|
super(options); |
|
|
|
|
|
|
|
Readable.call(this, options); |
|
|
|
this._inBody = false; |
|
|
|
this._sawFirstCr = false; |
|
|
|
|
|
|
@ -1049,9 +1045,9 @@ function SimpleProtocol(source, options) { |
|
|
|
|
|
|
|
this._rawHeader = []; |
|
|
|
this.header = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
SimpleProtocol.prototype._read = function(n) { |
|
|
|
_read(n) { |
|
|
|
if (!this._inBody) { |
|
|
|
var chunk = this._source.read(); |
|
|
|
|
|
|
@ -1108,8 +1104,8 @@ SimpleProtocol.prototype._read = function(n) { |
|
|
|
var chunk = this._source.read(); |
|
|
|
if (chunk) this.push(chunk); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// Usage: |
|
|
|
// var parser = new SimpleProtocol(source); |
|
|
|
// Now parser is a readable stream that will emit 'header' |
|
|
@ -1242,22 +1238,19 @@ would be piped into the parser, which is a more idiomatic Node.js stream |
|
|
|
approach. |
|
|
|
|
|
|
|
```javascript |
|
|
|
const util = require('util'); |
|
|
|
const Transform = require('stream').Transform; |
|
|
|
util.inherits(SimpleProtocol, Transform); |
|
|
|
|
|
|
|
function SimpleProtocol(options) { |
|
|
|
if (!(this instanceof SimpleProtocol)) |
|
|
|
return new SimpleProtocol(options); |
|
|
|
class SimpleProtocol extends Transform { |
|
|
|
constructor(options) { |
|
|
|
super(options); |
|
|
|
|
|
|
|
Transform.call(this, options); |
|
|
|
this._inBody = false; |
|
|
|
this._sawFirstCr = false; |
|
|
|
this._rawHeader = []; |
|
|
|
this.header = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
SimpleProtocol.prototype._transform = function(chunk, encoding, done) { |
|
|
|
_transform(chunk, encoding, done) { |
|
|
|
if (!this._inBody) { |
|
|
|
// check if the chunk has a \n\n |
|
|
|
var split = -1; |
|
|
@ -1300,8 +1293,8 @@ SimpleProtocol.prototype._transform = function(chunk, encoding, done) { |
|
|
|
this.push(chunk); |
|
|
|
} |
|
|
|
done(); |
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// Usage: |
|
|
|
// var parser = new SimpleProtocol(); |
|
|
|
// source.pipe(parser) |
|
|
@ -1636,23 +1629,19 @@ respectively. These options can be used to implement parsers and |
|
|
|
serializers with Transform streams. |
|
|
|
|
|
|
|
```js |
|
|
|
const util = require('util'); |
|
|
|
const StringDecoder = require('string_decoder').StringDecoder; |
|
|
|
const Transform = require('stream').Transform; |
|
|
|
util.inherits(JSONParseStream, Transform); |
|
|
|
|
|
|
|
// Gets \n-delimited JSON string data, and emits the parsed objects |
|
|
|
function JSONParseStream() { |
|
|
|
if (!(this instanceof JSONParseStream)) |
|
|
|
return new JSONParseStream(); |
|
|
|
|
|
|
|
Transform.call(this, { readableObjectMode : true }); |
|
|
|
class JSONParseStream extends Transform { |
|
|
|
constructor() { |
|
|
|
super({ readableObjectMode : true }); |
|
|
|
|
|
|
|
this._buffer = ''; |
|
|
|
this._decoder = new StringDecoder('utf8'); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
JSONParseStream.prototype._transform = function(chunk, encoding, cb) { |
|
|
|
_transform(chunk, encoding, cb) { |
|
|
|
this._buffer += this._decoder.write(chunk); |
|
|
|
// split on newlines |
|
|
|
var lines = this._buffer.split(/\r?\n/); |
|
|
@ -1670,9 +1659,9 @@ JSONParseStream.prototype._transform = function(chunk, encoding, cb) { |
|
|
|
this.push(obj); |
|
|
|
} |
|
|
|
cb(); |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
JSONParseStream.prototype._flush = function(cb) { |
|
|
|
_flush(cb) { |
|
|
|
// Just handle any leftover |
|
|
|
var rem = this._buffer.trim(); |
|
|
|
if (rem) { |
|
|
@ -1686,7 +1675,8 @@ JSONParseStream.prototype._flush = function(cb) { |
|
|
|
this.push(obj); |
|
|
|
} |
|
|
|
cb(); |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
### `stream.read(0)` |
|
|
@ -1739,7 +1729,6 @@ horribly wrong. |
|
|
|
[`stream.unpipe()`]: #stream_readable_unpipe_destination |
|
|
|
[`stream.wrap()`]: #stream_readable_wrap_stream |
|
|
|
[`tls.CryptoStream`]: tls.html#tls_class_cryptostream |
|
|
|
[`util.inherits()`]: util.html#util_util_inherits_constructor_superconstructor |
|
|
|
[API for Stream Consumers]: #stream_api_for_stream_consumers |
|
|
|
[API for Stream Implementors]: #stream_api_for_stream_implementors |
|
|
|
[child process stdin]: child_process.html#child_process_child_stdin |
|
|
|