You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
5.8 KiB
207 lines
5.8 KiB
3 years ago
|
'use strict';
|
||
|
|
||
|
var _Object$setPrototypeO;
|
||
|
|
||
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||
|
|
||
|
var finished = require('./end-of-stream');
|
||
|
|
||
|
var kLastResolve = Symbol('lastResolve');
|
||
|
var kLastReject = Symbol('lastReject');
|
||
|
var kError = Symbol('error');
|
||
|
var kEnded = Symbol('ended');
|
||
|
var kLastPromise = Symbol('lastPromise');
|
||
|
var kHandlePromise = Symbol('handlePromise');
|
||
|
var kStream = Symbol('stream');
|
||
|
|
||
|
function createIterResult(value, done) {
|
||
|
return {
|
||
|
value: value,
|
||
|
done: done
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function readAndResolve(iter) {
|
||
|
var resolve = iter[kLastResolve];
|
||
|
|
||
|
if (resolve !== null) {
|
||
|
var data = iter[kStream].read(); // we defer if data is null
|
||
|
// we can be expecting either 'end' or
|
||
|
// 'error'
|
||
|
|
||
|
if (data !== null) {
|
||
|
iter[kLastPromise] = null;
|
||
|
iter[kLastResolve] = null;
|
||
|
iter[kLastReject] = null;
|
||
|
resolve(createIterResult(data, false));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function onReadable(iter) {
|
||
|
// we wait for the next tick, because it might
|
||
|
// emit an error with process.nextTick
|
||
|
process.nextTick(readAndResolve, iter);
|
||
|
}
|
||
|
|
||
|
function wrapForNext(lastPromise, iter) {
|
||
|
return function (resolve, reject) {
|
||
|
lastPromise.then(function () {
|
||
|
if (iter[kEnded]) {
|
||
|
resolve(createIterResult(undefined, true));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
iter[kHandlePromise](resolve, reject);
|
||
|
}, reject);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
var AsyncIteratorPrototype = Object.getPrototypeOf(function () {});
|
||
|
var ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf((_Object$setPrototypeO = {
|
||
|
get stream() {
|
||
|
return this[kStream];
|
||
|
},
|
||
|
|
||
|
next: function next() {
|
||
|
var _this = this;
|
||
|
|
||
|
// if we have detected an error in the meanwhile
|
||
|
// reject straight away
|
||
|
var error = this[kError];
|
||
|
|
||
|
if (error !== null) {
|
||
|
return Promise.reject(error);
|
||
|
}
|
||
|
|
||
|
if (this[kEnded]) {
|
||
|
return Promise.resolve(createIterResult(undefined, true));
|
||
|
}
|
||
|
|
||
|
if (this[kStream].destroyed) {
|
||
|
// We need to defer via nextTick because if .destroy(err) is
|
||
|
// called, the error will be emitted via nextTick, and
|
||
|
// we cannot guarantee that there is no error lingering around
|
||
|
// waiting to be emitted.
|
||
|
return new Promise(function (resolve, reject) {
|
||
|
process.nextTick(function () {
|
||
|
if (_this[kError]) {
|
||
|
reject(_this[kError]);
|
||
|
} else {
|
||
|
resolve(createIterResult(undefined, true));
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
} // if we have multiple next() calls
|
||
|
// we will wait for the previous Promise to finish
|
||
|
// this logic is optimized to support for await loops,
|
||
|
// where next() is only called once at a time
|
||
|
|
||
|
|
||
|
var lastPromise = this[kLastPromise];
|
||
|
var promise;
|
||
|
|
||
|
if (lastPromise) {
|
||
|
promise = new Promise(wrapForNext(lastPromise, this));
|
||
|
} else {
|
||
|
// fast path needed to support multiple this.push()
|
||
|
// without triggering the next() queue
|
||
|
var data = this[kStream].read();
|
||
|
|
||
|
if (data !== null) {
|
||
|
return Promise.resolve(createIterResult(data, false));
|
||
|
}
|
||
|
|
||
|
promise = new Promise(this[kHandlePromise]);
|
||
|
}
|
||
|
|
||
|
this[kLastPromise] = promise;
|
||
|
return promise;
|
||
|
}
|
||
|
}, _defineProperty(_Object$setPrototypeO, Symbol.asyncIterator, function () {
|
||
|
return this;
|
||
|
}), _defineProperty(_Object$setPrototypeO, "return", function _return() {
|
||
|
var _this2 = this;
|
||
|
|
||
|
// destroy(err, cb) is a private API
|
||
|
// we can guarantee we have that here, because we control the
|
||
|
// Readable class this is attached to
|
||
|
return new Promise(function (resolve, reject) {
|
||
|
_this2[kStream].destroy(null, function (err) {
|
||
|
if (err) {
|
||
|
reject(err);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
resolve(createIterResult(undefined, true));
|
||
|
});
|
||
|
});
|
||
|
}), _Object$setPrototypeO), AsyncIteratorPrototype);
|
||
|
|
||
|
var createReadableStreamAsyncIterator = function createReadableStreamAsyncIterator(stream) {
|
||
|
var _Object$create;
|
||
|
|
||
|
var iterator = Object.create(ReadableStreamAsyncIteratorPrototype, (_Object$create = {}, _defineProperty(_Object$create, kStream, {
|
||
|
value: stream,
|
||
|
writable: true
|
||
|
}), _defineProperty(_Object$create, kLastResolve, {
|
||
|
value: null,
|
||
|
writable: true
|
||
|
}), _defineProperty(_Object$create, kLastReject, {
|
||
|
value: null,
|
||
|
writable: true
|
||
|
}), _defineProperty(_Object$create, kError, {
|
||
|
value: null,
|
||
|
writable: true
|
||
|
}), _defineProperty(_Object$create, kEnded, {
|
||
|
value: stream._readableState.endEmitted,
|
||
|
writable: true
|
||
|
}), _defineProperty(_Object$create, kHandlePromise, {
|
||
|
value: function value(resolve, reject) {
|
||
|
var data = iterator[kStream].read();
|
||
|
|
||
|
if (data) {
|
||
|
iterator[kLastPromise] = null;
|
||
|
iterator[kLastResolve] = null;
|
||
|
iterator[kLastReject] = null;
|
||
|
resolve(createIterResult(data, false));
|
||
|
} else {
|
||
|
iterator[kLastResolve] = resolve;
|
||
|
iterator[kLastReject] = reject;
|
||
|
}
|
||
|
},
|
||
|
writable: true
|
||
|
}), _Object$create));
|
||
|
iterator[kLastPromise] = null;
|
||
|
finished(stream, function (err) {
|
||
|
if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
|
||
|
var reject = iterator[kLastReject]; // reject if we are waiting for data in the Promise
|
||
|
// returned by next() and store the error
|
||
|
|
||
|
if (reject !== null) {
|
||
|
iterator[kLastPromise] = null;
|
||
|
iterator[kLastResolve] = null;
|
||
|
iterator[kLastReject] = null;
|
||
|
reject(err);
|
||
|
}
|
||
|
|
||
|
iterator[kError] = err;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var resolve = iterator[kLastResolve];
|
||
|
|
||
|
if (resolve !== null) {
|
||
|
iterator[kLastPromise] = null;
|
||
|
iterator[kLastResolve] = null;
|
||
|
iterator[kLastReject] = null;
|
||
|
resolve(createIterResult(undefined, true));
|
||
|
}
|
||
|
|
||
|
iterator[kEnded] = true;
|
||
|
});
|
||
|
stream.on('readable', onReadable.bind(null, iterator));
|
||
|
return iterator;
|
||
|
};
|
||
|
|
||
|
module.exports = createReadableStreamAsyncIterator;
|