Source code
Revision control
Copy as Markdown
Other Tools
'use strict';
function postInheritPriorityTestTask(config) {
const ids = [];
const task = scheduler.postTask(async () => {
await new Promise(resolve => setTimeout(resolve));
await fetch('/common/blank.html');
await new Promise(resolve => setTimeout(resolve));
const subtask = scheduler.postTask(() => { ids.push('subtask'); }, {priority: config.subTaskPriority});
await scheduler.yield();
ids.push('yield');
await subtask;
}, config.taskOptions);
return {task, ids}
}
for (let priority of ['user-blocking', 'background']) {
const expected = priority == 'user-blocking' ? 'yield,subtask' : 'subtask,yield';
promise_test(async t => {
const config = {
taskOptions: {priority},
subTaskPriority: 'user-blocking',
};
const {task, ids} = postInheritPriorityTestTask(config);
await task;
assert_equals(ids.join(), expected);
}, `yield() inherits priority (string) across promises (${priority})`);
promise_test(async t => {
const signal = (new TaskController({priority})).signal;
const config = {
taskOptions: {signal},
subTaskPriority: 'user-blocking',
};
const {task, ids} = postInheritPriorityTestTask(config);
await task;
assert_equals(ids.join(), expected);
}, `yield() inherits priority (signal) across promises (${priority})`);
}
promise_test(async t => {
const controller = new TaskController();
const signal = controller.signal;
return scheduler.postTask(async () => {
await new Promise(resolve => setTimeout(resolve));
await fetch('/common/blank.html');
await new Promise(resolve => setTimeout(resolve));
controller.abort();
const p = scheduler.yield();
await promise_rejects_dom(t, 'AbortError', p);
}, {signal});
}, `yield() inherits abort across promises`);
promise_test(async t => {
const ids = [];
let {promise: p1, resolve} = Promise.withResolvers();
// For promises, the scheduling state is bound to the future microtask when
// the promise is awaited or .then() is called on it. This tests that the
// right scheduling state is used, i.e. not the "resolve time" state.
//
// First, create a pending continuation (.then(...)) bound to the current
// (null) scheduling state. The continuation calls yield(), which should
// inherit the null scheduling state.
p1 = p1.then(async () => {
await scheduler.yield();
ids.push('continuation');
});
// Next, resolve `p1` in a user-blocking task. The user-blocking scheduling
// state should not be propagated to the continuation above.
await scheduler.postTask(resolve, {priority: 'user-blocking'});
// Finally, to test this, race another user-blocking task with the `p1`
// continuation above. The continuation should run after this task, since it
// should not inherit the user-blocking priority.
const p2 = scheduler.postTask(() => {
ids.push('task');
}, {priority: 'user-blocking'});
const result = await Promise.all([p1, p2]);
assert_equals(ids.toString(), 'task,continuation');
}, 'yield() inherits .then() context, not resolve context');
promise_test(async t => {
// This tests that non-promise microtasks also inherit scheduling state by
// checking that the scheduling state is propagated from queueMicrotask() to
// the subsequent microtask.
//
// First, create a pending continuation (.then(...)) which will be bound to
// the current (null) context. The yield() below should have default priority.
const ids = [];
let {promise: p1, resolve} = Promise.withResolvers();
p1 = p1.then(async () => {
ids.push('p1-start');
await scheduler.yield();
ids.push('p1-continuation');
});
// Next, schedule a task which resolves `p1` and then calls queueMicrotask().
// This is done to interleave the microtasks in a way that we can ensure
// queueMicrotask() actually propagates scheduling state, rather than using
// the state set when the postTask() callback starts.
//
// The yield() below should inherit the user-blocking priority.
const p2 = scheduler.postTask(async () => {
resolve();
queueMicrotask(async () => {
ids.push('p2-start');
await scheduler.yield();
ids.push('p2-continuation');
})
}, {priority: 'user-blocking'});
// Finally, schedule another task to race with the contents of the `p2` task
// above. Both yield() calls above happen during the `p2` task microtask
// checkpoint, so both continuations are scheduled when the `p3` task below
// runs. The p2-continuation (user-blocking continuation) should run before
// the `p3` task, and the p1-continuation (default prioriy continuation)
// should run after.
const p3 = scheduler.postTask(() => {
ids.push('p3');
}, {priority: 'user-blocking'});
await Promise.all([p1, p2, p3]);
assert_equals(
ids.toString(), "p1-start,p2-start,p2-continuation,p3,p1-continuation");
}, 'yield() inherits priority in queueMicrotask()');