c语言sscanf函数的用法是什么
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)插入workschedule方法从workList中取出work,传递给performperform方法执行完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,最高的同步优先级UserBlockingPriorityNormalPriorityLowPriorityIdlePriority,最低优先级
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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~