yield-inherit-across-promises.any.js (4955B)
1 'use strict'; 2 3 function postInheritPriorityTestTask(config) { 4 const ids = []; 5 const task = scheduler.postTask(async () => { 6 await new Promise(resolve => setTimeout(resolve)); 7 await fetch('/common/blank.html'); 8 await new Promise(resolve => setTimeout(resolve)); 9 const subtask = scheduler.postTask(() => { ids.push('subtask'); }, {priority: config.subTaskPriority}); 10 await scheduler.yield(); 11 ids.push('yield'); 12 await subtask; 13 }, config.taskOptions); 14 return {task, ids} 15 } 16 17 for (let priority of ['user-blocking', 'background']) { 18 const expected = priority == 'user-blocking' ? 'yield,subtask' : 'subtask,yield'; 19 promise_test(async t => { 20 const config = { 21 taskOptions: {priority}, 22 subTaskPriority: 'user-blocking', 23 }; 24 const {task, ids} = postInheritPriorityTestTask(config); 25 await task; 26 assert_equals(ids.join(), expected); 27 }, `yield() inherits priority (string) across promises (${priority})`); 28 29 promise_test(async t => { 30 const signal = (new TaskController({priority})).signal; 31 const config = { 32 taskOptions: {signal}, 33 subTaskPriority: 'user-blocking', 34 }; 35 const {task, ids} = postInheritPriorityTestTask(config); 36 await task; 37 assert_equals(ids.join(), expected); 38 }, `yield() inherits priority (signal) across promises (${priority})`); 39 } 40 41 promise_test(async t => { 42 const controller = new TaskController(); 43 const signal = controller.signal; 44 return scheduler.postTask(async () => { 45 await new Promise(resolve => setTimeout(resolve)); 46 await fetch('/common/blank.html'); 47 await new Promise(resolve => setTimeout(resolve)); 48 controller.abort(); 49 const p = scheduler.yield(); 50 await promise_rejects_dom(t, 'AbortError', p); 51 }, {signal}); 52 }, `yield() inherits abort across promises`); 53 54 promise_test(async t => { 55 const ids = []; 56 let {promise: p1, resolve} = Promise.withResolvers(); 57 // For promises, the scheduling state is bound to the future microtask when 58 // the promise is awaited or .then() is called on it. This tests that the 59 // right scheduling state is used, i.e. not the "resolve time" state. 60 // 61 // First, create a pending continuation (.then(...)) bound to the current 62 // (null) scheduling state. The continuation calls yield(), which should 63 // inherit the null scheduling state. 64 p1 = p1.then(async () => { 65 await scheduler.yield(); 66 ids.push('continuation'); 67 }); 68 // Next, resolve `p1` in a user-blocking task. The user-blocking scheduling 69 // state should not be propagated to the continuation above. 70 await scheduler.postTask(resolve, {priority: 'user-blocking'}); 71 // Finally, to test this, race another user-blocking task with the `p1` 72 // continuation above. The continuation should run after this task, since it 73 // should not inherit the user-blocking priority. 74 const p2 = scheduler.postTask(() => { 75 ids.push('task'); 76 }, {priority: 'user-blocking'}); 77 78 const result = await Promise.all([p1, p2]); 79 assert_equals(ids.toString(), 'task,continuation'); 80 }, 'yield() inherits .then() context, not resolve context'); 81 82 promise_test(async t => { 83 // This tests that non-promise microtasks also inherit scheduling state by 84 // checking that the scheduling state is propagated from queueMicrotask() to 85 // the subsequent microtask. 86 // 87 // First, create a pending continuation (.then(...)) which will be bound to 88 // the current (null) context. The yield() below should have default priority. 89 const ids = []; 90 let {promise: p1, resolve} = Promise.withResolvers(); 91 p1 = p1.then(async () => { 92 ids.push('p1-start'); 93 await scheduler.yield(); 94 ids.push('p1-continuation'); 95 }); 96 97 // Next, schedule a task which resolves `p1` and then calls queueMicrotask(). 98 // This is done to interleave the microtasks in a way that we can ensure 99 // queueMicrotask() actually propagates scheduling state, rather than using 100 // the state set when the postTask() callback starts. 101 // 102 // The yield() below should inherit the user-blocking priority. 103 const p2 = scheduler.postTask(async () => { 104 resolve(); 105 queueMicrotask(async () => { 106 ids.push('p2-start'); 107 await scheduler.yield(); 108 ids.push('p2-continuation'); 109 }) 110 }, {priority: 'user-blocking'}); 111 112 // Finally, schedule another task to race with the contents of the `p2` task 113 // above. Both yield() calls above happen during the `p2` task microtask 114 // checkpoint, so both continuations are scheduled when the `p3` task below 115 // runs. The p2-continuation (user-blocking continuation) should run before 116 // the `p3` task, and the p1-continuation (default prioriy continuation) 117 // should run after. 118 const p3 = scheduler.postTask(() => { 119 ids.push('p3'); 120 }, {priority: 'user-blocking'}); 121 122 await Promise.all([p1, p2, p3]); 123 assert_equals( 124 ids.toString(), "p1-start,p2-start,p2-continuation,p3,p1-continuation"); 125 }, 'yield() inherits priority in queueMicrotask()');