从测试看react源码_schedulerBrowser

主要测试 Scheduler 在浏览器中的运行。主要利用 MessageChannel API 进行异步执行 Task

environment

模拟浏览器 MessageChannel API,Mock了 port1 用于异步 onmessage,port2 用于 postmessage
在 port1的 onmessage 赋值了 performWorkUntilDeadline,此函数执行work直至当前 时间切片结束,默认时间切片时间为5ms,时间切片结束,将会把控制权交还给主线程,用于响应用户事件等。

1. task that finishes before deadline
1
2
3
4
5
6
7
8
it('task that finishes before deadline', () => {
scheduleCallback(NormalPriority, () => {
runtime.log('Task');
});
runtime.assertLog(['Post Message']);
runtime.fireMessageEvent();
runtime.assertLog(['Message Event', 'Task']);
});
2. task with continuation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
it('task with continuation', () => {
scheduleCallback(NormalPriority, () => {
runtime.log('Task');
while (!Scheduler.unstable_shouldYield()) {
runtime.advanceTime(1);
}
runtime.log(`Yield at ${performance.now()}ms`);
return () => {
runtime.log('Continuation');
};
});
runtime.assertLog(['Post Message']);

runtime.fireMessageEvent();
runtime.assertLog([
'Message Event',
'Task',
'Yield at 5ms',
'Post Message',
]);

runtime.fireMessageEvent();
runtime.assertLog(['Message Event', 'Continuation']);
});

callback 返回子work,但是会在下一个 message event 事件中执行

3. multiple tasks
1
2
3
4
5
6
7
8
9
10
11
it('multiple tasks', () => {
scheduleCallback(NormalPriority, () => {
runtime.log('A');
});
scheduleCallback(NormalPriority, () => {
runtime.log('B');
});
runtime.assertLog(['Post Message']);
runtime.fireMessageEvent();
runtime.assertLog(['Message Event', 'A', 'B']);
});
4. multiple tasks with a yield in between
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
it('multiple tasks with a yield in between', () => {
// 时间切片为5ms,当设置时间4999之后时,当前时间切片已经用完,所以yield之后,异步执行下一个task

scheduleCallback(NormalPriority, () => {
runtime.log('A');
runtime.advanceTime(4999);
});
scheduleCallback(NormalPriority, () => {
runtime.log('B');
});
runtime.assertLog(['Post Message']);
runtime.fireMessageEvent();
runtime.assertLog([
'Message Event',
'A',
// Ran out of time. Post a continuation event.
'Post Message',
]);
runtime.fireMessageEvent();
runtime.assertLog(['Message Event', 'B']);
});
5. throws when a task errors then continues in a new event
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
it('throws when a task errors then continues in a new event', () => {
scheduleCallback(NormalPriority, () => {
runtime.log('Oops!');
throw Error('Oops!');
});
scheduleCallback(NormalPriority, () => {
runtime.log('Yay');
});
runtime.assertLog(['Post Message']);

expect(() => runtime.fireMessageEvent()).toThrow('Oops!');
runtime.assertLog(['Message Event', 'Oops!', 'Post Message']);

runtime.fireMessageEvent();
runtime.assertLog(['Message Event', 'Yay']);
});

如果 task 执行报错,后面的任务会在下一个 message event 中继续执行 因为在 workLoop 中在执行任务前会先将 task.callback 设置为null,就是取消任务