100行代码实现React核心调度功能
作者:卡颂
简介:《React技术揭秘》作者
来源:SegmentFault 思否社区
大家好,我卡颂。
想必大家都知道React有一套基于Fiber架构的调度系统。
这套调度系统的基本功能包括:
更新有不同优先级
一次更新可能涉及多个组件的render,这些render可能分配到多个宏任务中执行(即时间切片)
高优先级更新会打断进行中的低优先级更新
本文会用100行代码实现这套调度系统,让你快速了解React的调度原理。
我知道你不喜欢看大段的代码,所以本文会以图+代码片段的形式讲解原理。
文末有完整的在线Demo,你可以自己上手玩玩。
开整!
准备工作
我们用work这一数据结构代表一份工作,work.count代表这份工作要重复做某件事的次数。
在Demo中要重复做的事是“执行insertItem方法,向页面插入<span/>”:
const insertItem = (content: string) => {
const ele = document.createElement('span');
ele.innerText = `${content}`;
contentBox.appendChild(ele);
};
所以,对于如下work:
const work1 = {
count: 100
}
代表:执行100次insertItem向页面插入100个<span/>。
work可以类比React的一次更新,work.count类比这次更新要render的组件数量。所以Demo是对React更新流程的类比。
来实现第一版的调度系统,流程如图:
包括三步:
向workList队列(用于保存所有work)插入work
schedule方法从workList中取出work,传递给perform
perform方法执行完work的所有工作后重复步骤2
// 保存所有work的队列
const workList: work[] = [];
// 调度
function schedule() {
// 从队列尾取一个work
const curWork = workList.pop();
if (curWork) {
perform(curWork);
}
}
// 执行
function perform(work: Work) {
while (work.count) {
work.count--;
insertItem();
}
schedule();
}
button.onclick = () => {
workList.unshift({
count: 100
})
schedule();
}
Scheduler
ImmediatePriority,最高的同步优先级
UserBlockingPriority
NormalPriority
LowPriority
IdlePriority,最低优先级
// 将回调函数fn以LowPriority优先级调度
scheduleCallback(LowPriority, fn)
const task1 = {
expiration: startTime + timeout,
callback: fn
}
startTime - 1 < startTime
用Scheduler改造Demo
// 改造前
const curWork = workList.pop();
const work1 = {
count: 100,
priority: NormalPriority
}
// 改造后
// 对workList排序后取priority值最小的(值越小,优先级越高)
const curWork = workList.sort((w1, w2) => {
return w1.priority - w2.priority;
})[0];
改造后流程的变化
const someTask = {
callback: perform.bind(null, work),
expiration: xxx
}
while (work.count) {
work.count--;
insertItem();
}
while (!needYield() && work.count) {
work.count--;
insertItem();
}
高优先级打断低优先级的例子
插入一个低优先级work,属性如下
const work1 = {
count: 100,
priority: LowPriority
}
经历schedule(调度),perform(执行),在执行了80次工作时,突然插入一个高优先级work,此时:
const work1 = {
// work1已经执行了80次工作,还差20次执行完
count: 20,
priority: LowPriority
}
// 新插入的高优先级work
const work2 = {
count: 100,
priority: ImmediatePriority
}
work1工作中断,继续schedule。由于work2优先级更高,会进入work2对应perform,执行100次工作
work2执行完后,继续schedule,执行work1剩余的20次工作
在步骤3中,work1执行的工作被打断。这是微观角度的打断
由于work1被打断,所以继续schedule。下一个执行工作的是更高优的work2。work2的到来导致work1被打断,这是宏观角度的打断
调度系统的实现原理
总结
schedule perform