promised-read
Read from a stream using Promises, with support for timeouts, cancellation, and several ways to determine how much data to read.
Introductory Example
var readTo = readTo;processstderr;;
Features
- Supports pre-0.10 (v1) streams and post-0.10 (v2) streams.
- Supports
objectMode
streams and decoded string streams in addition to byte (Buffer
) streams. - Supports sized reads.
- Supports reading to the end of stream.
- Supports reading up to an expected sequence (with unshift for over-reads).
- Supports reading until a function is satisfied (with unshift for over-reads).
- Supports reading until a RegExp is matched (with unshift for over-reads).
- Supports synchronous promise resolution to avoid missing events for pre-0.10 streams (see caveats below).
- Supports read timeout and read cancellation (including bluebird 3.x cancellation integration, when available - see caveats below).
Installation
This package can be installed using npm, either globally or locally, by running:
npm install promised-read
Recipes
Sized read
var fs = ;var read = read;var input = fs;;
Read to end
var fs = ;var readToEnd = readToEnd;var input = fs; ;
Read lines
var fs = ;var readToMatch = readToMatch;var file = fs; { return ;} { var numbers = ; var ended = false; file; return ;};
Read Until
var fs = ;var readUntil = readUntil;var input = fs;// Don't use this without handling '{' and '}' in strings { var depth = 0; for var i = 0; i < datalength; ++i var ch = datai; if ch === '{' ++depth; else if ch === '}' --depth; if depth === 0 // Length including closing bracket. Additional data will be unshifted. return i + 1; return -1;};
Read Until (incremental, faster)
The until
function can also operate on the individual chunks read, which are
passed as the second argument. This avoids re-processing data which was
previously checked. The previous example can be made more efficient with:
var fs = ;var readUntil = readUntil;var input = fs;var depth = 0;// Don't use this without handling '{' and '}' in strings { if !chunk // chunk === null and ended === true when called for the 'end' event // (chunk === null could also happen on pre-0.10 streams in objectMode) return -1; for var i = 0; i < chunklength; ++i var ch = chunki; if ch === '{' ++depth; else if ch === '}' --depth; if depth === 0 // Length including closing bracket. Additional data will be unshifted. return datalength - chunklength + i + 1; return -1;};
Read with Timeout
var readTo = readTo;processstderr;var promise = ;promise;
Note: Several promise libraries provide a .timeout()
method which
creates a chained promise which is rejected after a delay. Although this
works for the read consumer, it won't cancel the read operation, causing any
data read from the stream (even after the timeout) to be discarded. A notable
exception is bluebird when
cancellation is enabled,
which behaves as if .cancel()
was called on the original promise (see
caveats below).
Abort/Cancel Support
Although generic Promise cancellation support is still far from being
standardized (see
cancellation-spec,
async cancel, and
fetch abort for discussion), this
module provides an optional module-specific mechanism for callers to abort or
cancel a pending read operation. Passing a truthy value for
options.cancellation
causes the returned Promise
to provide two additional
methods:
.abortRead()
causes reading to cease, the promise to be rejected with anAbortError
, and any previously read data to be unshifted. If unshift is not supported or causes an error, the data is set as the.read
property of theAbortError
..cancelRead()
causes the reading to cease and any previously read data to be unshifted. The promise will never be resolved or rejected. If unshift is not supported or causes an error, the data will be returned from.cancelRead()
.
Note: These methods grant all promise holders the authority to abort or
cancel the read operation for all observers (including the unshift
side-effects). Any consumers of the read result which which do not require
abort/cancel authority should be given a Promise chained from the returned one
(e.g. the result of calling .then()
on it) to avoid granting
abort/cancel authority for the read.
Abort reading
var assert = ;var read = read;var stream = ;var input = ;var promise = ;promise;promise;// reading is stopped immediately, so any future writes are unaffectedinput;console;
Cancel reading
var assert = ;var read = read;var stream = ;var input = ;var promise = ;promise;promise;// reading is stopped immediately, so any future writes are unaffectedinput;console;
until
Abort from The until
argument to readUntil
can also cause read to be aborted by
throwing an exception. In this case, the promise is rejected with the
exception thrown by until
rather than an AbortError
.
var fs = ;var readUntil = readUntil;var zlib = ;var input = fs; { if datalength >= 2 && data0 !== 0x1f && data1 !== 0x8b throw 'invalid gzip header'; };
bluebird
3.x Cancellation
This module also cancels the read operation when the returned promise is
cancelled via bluebird 3.x
cancellation. However, use
of this method is not recommended due to a delay between when .cancel()
is
called and when the onCancel
listener is called which can lead to lost data
(see #1041).
var bluebird = ;var readTo = readTo;bluebird;processstderr;var promise = ;promise;processstderr;// Note1: Only use .cancel() if the stream supports unshift or it would be// safe to discard any data which has already been read// Note2: Due to the delay between .cancel() and the onCancel listener,// callers should either delay writing for at least one tick or watch for// 'data' events during that tick to avoid losing data.// See https://github.com/petkaantonov/bluebird/issues/1041promise;
More examples can be found in the test specifications.
API Docs
To use this module as a library, see the API Documentation.
API Warnings
Reading after end
The stream API does not provide a way to
differentiate between a stream which has ended and one which does not
currently have data available. For this reason, the read
functions should
not be called after a stream emits 'end'
or 'error'
. It is the caller's
responsibility to ensure that read
is only called on streams in a readable
state.
Synchronous resolution for flowing streams
When reading from streams which lack a .read()
method, or when
options.flowing
is true
, the onFulfilled
and onRejected
functions
(i.e. functions added with .then()
or .catch()
) will be called
synchronously upon promise resolution or rejection. This is necessary to
prevent missing stream events between resolution and the handler being called,
when another read can be started.
This behavior may be surprising, since it conflicts with point 2.2.4 of the
Promises/A+ spec (for an example, see A
Promises/A Test
Suite). However,
since read promises are only resolved synchronously when an argument error
occurs, one of the most common pitfalls is avoided. Additionally, once the
synchronous behavior is not necessary (e.g. after the next read has started)
asynchronous behavior can be restored by chaining to another promise class
(e.g. Promise.resolve(readPromise)
).
Synchronous promises can also be avoided by specifying an asynchronous promise
constructor via options.Promise
. However, this is likely to cause missed
events if the writer is not synchronized with with reader.
Contributing
Contributions are appreciated. Contributors agree to abide by the Contributor Covenant Code of Conduct. If this is your first time contributing to a Free and Open Source Software project, consider reading How to Contribute to Open Source in the Open Source Guides.
If the desired change is large, complex, backwards-incompatible, can have significantly differing implementations, or may not be in scope for this project, opening an issue before writing the code can avoid frustration and save a lot of time and effort.
License
This project is available under the terms of the MIT License. See the summary at TLDRLegal.