100行代码实现React核心调度功能

网友投稿 302 2022-11-22

100行代码实现React核心调度功能

大家好,我卡颂。

想必大家都知道​​React​​​有一套基于​​Fiber​​架构的调度系统。

这套调度系统的基本功能包括:

更新有不同优先级一次更新可能涉及多个组件的​​render​​​,这些​​render​​​可能分配到多个​​宏任务​​​中执行(即​​时间切片​​)​​高优先级更新​​​会打断进行中的​​低优先级更新​​

本文会用100行代码实现这套调度系统,让你快速了解​​React​​的调度原理。

我知道你不喜欢看大段的代码,所以本文会以​​图​​​+​​代码片段​​的形式讲解原理。

文末有完整的​​在线Demo​​,你可以自己上手玩玩。

开整!

准备工作

我们用​​work​​​这一数据结构代表一份工作,​​work.count​​代表这份工作要重复做某件事的次数。

在​​Demo​​​中要重复做的事是“执行​​insertItem​​​方法,向页面插入​​​​”:

const insertItem = (content: string) => { const ele = document.createElement('span'); ele.innerText = `${content}`; contentBox.appendChild(ele);};

所以,对于如下​​work​​:

const work1 = { count: 100}

代表:执行100次​​insertItem​​​向页面插入100个​​​​。

​​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

​​React​​​内部使用​​Scheduler​​完成异步调度。

​​Scheduler​​​是独立的包。所以可以用他改造我们的​​Demo​​。

​​Scheduler​​预置了5种优先级,从上往下优先级降低:

​​ImmediatePriority​​,最高的同步优先级​​UserBlockingPriority​​​​NormalPriority​​​​LowPriority​​​​IdlePriority​​,最低优先级

​​scheduleCallback​​​方法接收​​优先级​​​与回调函数​​fn​​​,用于调度​​fn​​:

// 将回调函数fn以LowPriority优先级调度scheduleCallback(LowPriority, fn)

在​​Scheduler​​​内部,执行​​scheduleCallback​​​后会生成​​task​​这一数据结构:

const task1 = { expiration: startTime + timeout, callback: fn}

​​task1.expiration​​​代表​​task1​​​的过期时间,​​Scheduler​​​会优先执行过期的​​task.callback​​。

​​expiration​​​中​​startTime​​​为当前开始时间,不同优先级的​​timeout​​不同。

比如,​​ImmediatePriority​​​的​​timeout​​为-1,由于:

startTime - 1 < startTime

所以​​ImmediatePriority​​​会立刻过期,​​callback​​立刻执行。

而​​IdlePriority​​​对应​​timeout​​​为1073741823(最大的31位带符号整型),其​​callback​​需要非常长时间才会执行。

​​callback​​​会在新的​​宏任务​​​中执行,这就是​​Scheduler​​调度的原理。

用Scheduler改造Demo

改造后的流程如图:

改造前,​​work​​​直接从​​workList​​队列尾取出:

// 改造前const curWork = workList.pop();

改造后,​​work​​​可以拥有不同​​优先级​​​,通过​​priority​​字段表示。

比如,如下​​work​​代表以NormalPriority优先级插入100个\

const work1 = { count: 100, priority: NormalPriority}

所以,改造后每次都使用最高优先级的​​work​​:

// 改造后// 对workList排序后取priority值最小的(值越小,优先级越高)const curWork = workList.sort((w1, w2) => { return w1.priority - w2.priority;})[0];

改造后流程的变化

由流程图可知,​​Scheduler​​​不再直接执行​​perform​​​,而是通过执行​​scheduleCallback​​​调度​​perform.bind(null, work)​​。

即,满足一定条件的情况下,生成新​​task​​:

const someTask = { callback: perform.bind(null, work), expiration: xxx}

同时,​​work​​​的工作也是可中断的。在改造前,​​perform​​​会同步执行完​​work​​中的所有工作:

while (work.count) { work.count--; insertItem();}

改造后,​​work​​的执行流程随时可能中断:

while (!needYield() && work.count) { work.count--; insertItem();}

​​needYield​​​方法的实现(何时会中断)请参考文末​​​在线Demo​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Snap inc正准备向AR/VR游戏开发领域的玩家发起挑战
下一篇:Hadoop-2.2.0学习之一Hadoop-2.2.0变化简介
相关文章

 发表评论

暂时没有评论,来抢沙发吧~