1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 16:42:21 -05:00

fix(ext/web/streams): fix ReadableStream asyncIterator (#16276)

This commit is contained in:
Marcos Casagrande 2023-01-09 21:17:36 +01:00 committed by David Sherret
parent fab10ea3e0
commit 49b5ac947f
2 changed files with 84 additions and 38 deletions

View file

@ -32,6 +32,7 @@
ObjectDefineProperties, ObjectDefineProperties,
ObjectDefineProperty, ObjectDefineProperty,
ObjectGetPrototypeOf, ObjectGetPrototypeOf,
ObjectPrototype,
ObjectPrototypeIsPrototypeOf, ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf, ObjectSetPrototypeOf,
Promise, Promise,
@ -4424,7 +4425,7 @@
* @returns {IteratorResult<T>} * @returns {IteratorResult<T>}
*/ */
function createIteratorResult(value, done) { function createIteratorResult(value, done) {
const result = ObjectCreate(null); const result = ObjectCreate(ObjectPrototype);
ObjectDefineProperties(result, { ObjectDefineProperties(result, {
value: { value, writable: true, enumerable: true, configurable: true }, value: { value, writable: true, enumerable: true, configurable: true },
done: { done: {
@ -4442,57 +4443,99 @@
ObjectGetPrototypeOf(async function* () {}).prototype, ObjectGetPrototypeOf(async function* () {}).prototype,
); );
const _iteratorNext = Symbol("[[iteratorNext]]");
const _iteratorFinished = Symbol("[[iteratorFinished]]");
/** @type {AsyncIterator<unknown>} */ /** @type {AsyncIterator<unknown>} */
const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({ const readableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({
/** @returns {Promise<IteratorResult<unknown>>} */ /** @returns {Promise<IteratorResult<unknown>>} */
next() { next() {
/** @type {ReadableStreamDefaultReader} */ /** @type {ReadableStreamDefaultReader} */
const reader = this[_reader]; const reader = this[_reader];
if (reader[_stream] === undefined) { function nextSteps() {
return PromiseReject( if (reader[_iteratorFinished]) {
new TypeError( return PromiseResolve(createIteratorResult(undefined, true));
"Cannot get the next iteration result once the reader has been released.", }
),
); if (reader[_stream] === undefined) {
return PromiseReject(
new TypeError(
"Cannot get the next iteration result once the reader has been released.",
),
);
}
/** @type {Deferred<IteratorResult<any>>} */
const promise = new Deferred();
/** @type {ReadRequest} */
const readRequest = {
chunkSteps(chunk) {
promise.resolve(createIteratorResult(chunk, false));
},
closeSteps() {
readableStreamDefaultReaderRelease(reader);
promise.resolve(createIteratorResult(undefined, true));
},
errorSteps(e) {
readableStreamDefaultReaderRelease(reader);
promise.reject(e);
},
};
readableStreamDefaultReaderRead(reader, readRequest);
return PromisePrototypeThen(promise.promise, (result) => {
reader[_iteratorNext] = null;
if (result.done === true) {
reader[_iteratorFinished] = true;
return createIteratorResult(undefined, true);
}
return result;
}, (reason) => {
reader[_iteratorNext] = null;
reader[_iteratorFinished] = true;
throw reason;
});
} }
/** @type {Deferred<IteratorResult<any>>} */
const promise = new Deferred(); reader[_iteratorNext] = reader[_iteratorNext]
/** @type {ReadRequest} */ ? PromisePrototypeThen(reader[_iteratorNext], nextSteps, nextSteps)
const readRequest = { : nextSteps();
chunkSteps(chunk) {
promise.resolve(createIteratorResult(chunk, false)); return reader[_iteratorNext];
},
closeSteps() {
readableStreamDefaultReaderRelease(reader);
promise.resolve(createIteratorResult(undefined, true));
},
errorSteps(e) {
readableStreamDefaultReaderRelease(reader);
promise.reject(e);
},
};
readableStreamDefaultReaderRead(reader, readRequest);
return promise.promise;
}, },
/** /**
* @param {unknown} arg * @param {unknown} arg
* @returns {Promise<IteratorResult<unknown>>} * @returns {Promise<IteratorResult<unknown>>}
*/ */
async return(arg) { return(arg) {
/** @type {ReadableStreamDefaultReader} */ /** @type {ReadableStreamDefaultReader} */
const reader = this[_reader]; const reader = this[_reader];
if (reader[_stream] === undefined) { const returnSteps = () => {
return createIteratorResult(undefined, true); if (reader[_iteratorFinished]) {
} return PromiseResolve(createIteratorResult(arg, true));
assert(reader[_readRequests].length === 0); }
if (this[_preventCancel] === false) { reader[_iteratorFinished] = true;
const result = readableStreamReaderGenericCancel(reader, arg);
if (reader[_stream] === undefined) {
return PromiseResolve(createIteratorResult(undefined, true));
}
assert(reader[_readRequests].length === 0);
if (this[_preventCancel] === false) {
const result = readableStreamReaderGenericCancel(reader, arg);
readableStreamDefaultReaderRelease(reader);
return result;
}
readableStreamDefaultReaderRelease(reader); readableStreamDefaultReaderRelease(reader);
await result; return PromiseResolve(createIteratorResult(undefined, true));
return createIteratorResult(arg, true); };
}
readableStreamDefaultReaderRelease(reader); const returnPromise = reader[_iteratorNext]
return createIteratorResult(undefined, true); ? PromisePrototypeThen(reader[_iteratorNext], returnSteps, returnSteps)
: returnSteps();
return PromisePrototypeThen(
returnPromise,
() => createIteratorResult(arg, true),
);
}, },
}, asyncIteratorPrototype); }, asyncIteratorPrototype);

View file

@ -1311,7 +1311,10 @@
"respond-after-enqueue.any.worker.html": true "respond-after-enqueue.any.worker.html": true
}, },
"readable-streams": { "readable-streams": {
"async-iterator.any.html": false, "async-iterator.any.html": [
"next() that succeeds; return()",
"next() that succeeds; return() [no awaiting]"
],
"bad-strategies.any.html": true, "bad-strategies.any.html": true,
"bad-strategies.any.worker.html": true, "bad-strategies.any.worker.html": true,
"bad-underlying-sources.any.html": true, "bad-underlying-sources.any.html": true,