Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- This WPT test may be referenced by the following Test IDs:
- /scheduler/tentative/yield/yield-inherit-abort-across-settled-promises.any.html - WPT Dashboard Interop Dashboard
- /scheduler/tentative/yield/yield-inherit-abort-across-settled-promises.any.worker.html - WPT Dashboard Interop Dashboard
'use strict';
// Companion to yield-inherit-across-settled-promises.any.js, covering the
// other half of the scheduling state: the abort source.
//
// A WebTaskSchedulingState carries both a priority source and an abort source,
// so a lost state costs the continuation its abort link, not just its
// priority.
//
// The abort must come from a *separate, later* task. Aborting from inside the
// enclosing task would also reject the postTask() result independently of the
// scheduling state, masking a missing link on the continuation itself.
function abortFromSeparateTask(controller) {
scheduler.postTask(() => { controller.abort(); },
{priority: 'user-blocking'});
}
// yield() is queued first, then aborted from a later task, so only an abort
// source inherited through the continuation can reject it.
function yieldThenAbort(t, controller) {
abortFromSeparateTask(controller);
t.step(() => assert_false(controller.signal.aborted));
return promise_rejects_dom(t, 'AbortError', scheduler.yield());
}
// The enclosing task's promise may resolve normally: the abort lands after the
// callback has started, so postTask() resolves with whatever the callback
// returns. The assertion that matters is inside yieldThenAbort(), on the
// continuation itself, so the task's promise is awaited only to surface errors
// from the body.
function abortInheritanceTest(name, body) {
promise_test(async t => {
const controller = new TaskController();
await scheduler.postTask(() => body(t, controller),
{signal: controller.signal});
}, name);
}
abortInheritanceTest('yield() inherits abort with no intervening promise',
(t, controller) => yieldThenAbort(t, controller));
abortInheritanceTest('yield() inherits abort across a pending promise',
async (t, controller) => {
await new Promise(resolve => queueMicrotask(resolve));
await yieldThenAbort(t, controller);
});
// Already-settled promises.
abortInheritanceTest('yield() inherits abort across an await of a settled promise',
async (t, controller) => {
await Promise.resolve();
await yieldThenAbort(t, controller);
});
abortInheritanceTest('yield() inherits abort across an await of a non-promise',
async (t, controller) => {
await null;
await yieldThenAbort(t, controller);
});
// yield() is called *inside* the reaction, since awaiting the result of then()
// would suspend on the pending promise then() returns and restore the state.
abortInheritanceTest('yield() inherits abort in then() on a settled promise',
(t, controller) => Promise.resolve().then(
() => yieldThenAbort(t, controller)));
abortInheritanceTest('yield() inherits abort in a chained then() on a settled promise',
(t, controller) => Promise.resolve().then(() => {}).then(
() => yieldThenAbort(t, controller)));
abortInheritanceTest('yield() inherits abort across an await of a thenable',
async (t, controller) => {
await {then(resolve) { resolve(); }};
await yieldThenAbort(t, controller);
});
abortInheritanceTest('yield() inherits abort in a thenable then()',
(t, controller) => new Promise(resolve => resolve({
then(res) { res(yieldThenAbort(t, controller)); }
})));