Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -21903,10 +21903,11 @@ static void js_async_generator_resume_next(JSContext *ctx,
} else if (next->completion_type == GEN_MAGIC_RETURN) {
s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
js_async_generator_completed_return(ctx, s, next->result);
goto done;
} else {
js_async_generator_reject(ctx, s, next->result);
}
goto done;
break;
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
value = js_dup(next->result);
Expand Down Expand Up @@ -21997,6 +21998,7 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx,
} else {
js_async_generator_resolve(ctx, s, arg, true);
}
js_async_generator_resume_next(ctx, s);
} else if (s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING) {
/* restart function execution after await() */
s->func_state.throw_flag = is_reject;
Expand Down
62 changes: 62 additions & 0 deletions tests/async-generator-drain-queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { assert } from "./assert.js";

/* AsyncGeneratorAwaitReturn and AsyncGeneratorDrainQueue: once the generator
has completed, every request queued behind a return() must still settle,
in order. A request that never settles leaves the module promise pending. */

async function completed() {
const it = (async function* () {})();
assert((await it.next()).done, true);
return it;
}

function track(log, label, promise) {
return promise.then(
v => { log.push(label + ":" + v.value + ":" + v.done); },
e => { log.push(label + ":throw:" + e); });
}

async function run(requests) {
const it = await completed();
const log = [];
await Promise.all(requests(it).map(([label, p]) => track(log, label, p)));
return log.join(",");
}

/* a single request behind the awaited return value */
assert(await run(it => [["return", it.return(1)], ["next", it.next()]]),
"return:1:true,next:undefined:true");
assert(await run(it => [["return", it.return(1)], ["return", it.return(2)]]),
"return:1:true,return:2:true");
assert(await run(it => [["return", it.return(1)], ["throw", it.throw("e")]]),
"return:1:true,throw:throw:e");
assert(await run(it => [["return", it.return(Promise.reject("r"))],
["return", it.return(2)]]),
"return:throw:r,return:2:true");

/* several requests: the queue is drained past next() and throw() */
assert(await run(it => [["return", it.return(1)], ["next", it.next()],
["next", it.next()]]),
"return:1:true,next:undefined:true,next:undefined:true");
assert(await run(it => [["return", it.return(1)], ["throw", it.throw("e")],
["next", it.next()]]),
"return:1:true,throw:throw:e,next:undefined:true");
assert(await run(it => [["return", it.return(Promise.reject("r"))],
["next", it.next()], ["return", it.return(2)]]),
"return:throw:r,next:undefined:true,return:2:true");
assert(await run(it => [["return", it.return(1)], ["next", it.next()],
["return", it.return(2)], ["next", it.next()]]),
"return:1:true,next:undefined:true,return:2:true,next:undefined:true");

/* requests queued while the body is still running reach a completed
generator without any return() call */
{
const gate = Promise.withResolvers();
const it = (async function* () { await gate.promise; })();
const log = [];
const requests = [1, 2, 3].map(i => track(log, "next" + i, it.next()));
gate.resolve();
await Promise.all(requests);
assert(log.join(","),
"next1:undefined:true,next2:undefined:true,next3:undefined:true");
}
Loading