100行代碼實(shí)現(xiàn)React核心調(diào)度功能
作者:卡頌
簡(jiǎn)介:《React技術(shù)揭秘》作者
來(lái)源:SegmentFault 思否社區(qū)
大家好,我卡頌。
想必大家都知道React有一套基于Fiber架構(gòu)的調(diào)度系統(tǒng)。
這套調(diào)度系統(tǒng)的基本功能包括:
更新有不同優(yōu)先級(jí)
一次更新可能涉及多個(gè)組件的render,這些render可能分配到多個(gè)宏任務(wù)中執(zhí)行(即時(shí)間切片)
高優(yōu)先級(jí)更新會(huì)打斷進(jìn)行中的低優(yōu)先級(jí)更新
本文會(huì)用100行代碼實(shí)現(xiàn)這套調(diào)度系統(tǒng),讓你快速了解React的調(diào)度原理。
我知道你不喜歡看大段的代碼,所以本文會(huì)以圖+代碼片段的形式講解原理。
文末有完整的在線Demo,你可以自己上手玩玩。
開(kāi)整!
準(zhǔn)備工作
我們用work這一數(shù)據(jù)結(jié)構(gòu)代表一份工作,work.count代表這份工作要重復(fù)做某件事的次數(shù)。
在Demo中要重復(fù)做的事是“執(zhí)行insertItem方法,向頁(yè)面插入<span/>”:
const insertItem = (content: string) => {
const ele = document.createElement('span');
ele.innerText = `${content}`;
contentBox.appendChild(ele);
};
所以,對(duì)于如下work:
const work1 = {
count: 100
}
代表:執(zhí)行100次insertItem向頁(yè)面插入100個(gè)<span/>。
work可以類(lèi)比React的一次更新,work.count類(lèi)比這次更新要render的組件數(shù)量。所以Demo是對(duì)React更新流程的類(lèi)比。
來(lái)實(shí)現(xiàn)第一版的調(diào)度系統(tǒng),流程如圖:

包括三步:
向workList隊(duì)列(用于保存所有work)插入work
schedule方法從workList中取出work,傳遞給perform
perform方法執(zhí)行完work的所有工作后重復(fù)步驟2
// 保存所有work的隊(duì)列
const workList: work[] = [];
// 調(diào)度
function schedule() {
// 從隊(duì)列尾取一個(gè)work
const curWork = workList.pop();
if (curWork) {
perform(curWork);
}
}
// 執(zhí)行
function perform(work: Work) {
while (work.count) {
work.count--;
insertItem();
}
schedule();
}
button.onclick = () => {
workList.unshift({
count: 100
})
schedule();
}
Scheduler
ImmediatePriority,最高的同步優(yōu)先級(jí)
UserBlockingPriority
NormalPriority
LowPriority
IdlePriority,最低優(yōu)先級(jí)
// 將回調(diào)函數(shù)fn以LowPriority優(yōu)先級(jí)調(diào)度
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
}
// 改造后
// 對(duì)workList排序后取priority值最小的(值越小,優(yōu)先級(jí)越高)
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();
}
高優(yōu)先級(jí)打斷低優(yōu)先級(jí)的例子
插入一個(gè)低優(yōu)先級(jí)work,屬性如下
const work1 = {
count: 100,
priority: LowPriority
}
經(jīng)歷schedule(調(diào)度),perform(執(zhí)行),在執(zhí)行了80次工作時(shí),突然插入一個(gè)高優(yōu)先級(jí)work,此時(shí):
const work1 = {
// work1已經(jīng)執(zhí)行了80次工作,還差20次執(zhí)行完
count: 20,
priority: LowPriority
}
// 新插入的高優(yōu)先級(jí)work
const work2 = {
count: 100,
priority: ImmediatePriority
}
work1工作中斷,繼續(xù)schedule。由于work2優(yōu)先級(jí)更高,會(huì)進(jìn)入work2對(duì)應(yīng)perform,執(zhí)行100次工作
work2執(zhí)行完后,繼續(xù)schedule,執(zhí)行work1剩余的20次工作
在步驟3中,work1執(zhí)行的工作被打斷。這是微觀角度的打斷
由于work1被打斷,所以繼續(xù)schedule。下一個(gè)執(zhí)行工作的是更高優(yōu)的work2。work2的到來(lái)導(dǎo)致work1被打斷,這是宏觀角度的打斷
調(diào)度系統(tǒng)的實(shí)現(xiàn)原理


總結(jié)
schedule perform

