当前位置: 首页 > Web前端 > HTML

Web前端实训:React核心调度功能的实现

时间:2023-03-28 00:48:16 HTML

大家一定知道React有一个基于Fiber架构的调度系统。该调度系统的基本功能包括:更新具有不同的优先级。更新可能涉及多个组件的呈现。这些渲染可以分配给多个宏任务来执行(即,时间分片)。高优先级更新会打断正在进行的低优先级更新本文将用100行代码来实现这个调度系统,让你快速了解React的调度原理。我知道你不喜欢看大段代码,所以本文将以图片+代码片段的形式进行讲解。文末有完整的在线Demo,大家可以自己玩玩。打开!Preparation我们使用work的数据结构来表示一个job,work.count表示这个job需要重复的次数_前端训练。demo中要重复的是“执行insertItem方法将插入到页面中”:constinsertItem=(content:string)=>{constele=document.createElement('span');ele。innerText=${content};contentBox.appendChild(ele);};因此,对于下面的工作:constwork1={count:100}意思是:执行100次insertItem,在页面中插入100个。work可以类比为React的一次更新,work.count就像这次更新要渲染的组件数量。所以Demo是类比React的更新过程来实现第一版的调度系统。流程如图:包括三个步骤:将workschedule方法插入到workList队列中(用于保存所有工作)从workList中取出工作,传递给performperform方法执行,完成所有工作后重复步骤2.代码如下://保存所有工作队列constworkList:work[]=[];//调度函数schedule(){//从队尾取一个工作constcurWork=workList.pop();if(curWork){perform(curWork);}}//执行函数perform(work:Work){while(work.count){work.count--;insertItem();}schedule();}是按钮绑定点击交互,最基本的调度系统就完成了:button.onclick=()=>{workList.unshift({count:100})schedule();}点击按钮插入100。React类比是:点击按钮,触发同步更新,渲染100个组件。接下来,我们将其转化为异步。SchedulerReact内部使用Scheduler完成异步调度。调度程序是一个独立的包。所以我们可以利用他来改造我们的Demo。Scheduler预设了5个优先级,从上到下递减:ImmediatePriority,同步优先级最高,UserBlockingPriorityNormalPriorityLowPriorityIdlePriority,优先级最低。scheduleCallback方法接收优先级和回调函数fn,用于调度fn://回调函数fn调度scheduleCallback(LowPriority,fn),优先级为LowPriority,在Scheduler内部。执行scheduleCallback后,会生成task的数据结构:consttask1={expiration:startTime+timeout,callback:fn}task1.expiration表示task1的过期时间,Scheduler会先执行过期的task.callback。expiration中的startTime为当前开始时间,不同优先级的超时时间不同。比如ImmediatePriority的timeout为-1,因为:startTime-1withNormalPrioritypriority”:constwork1={count:100,priority:NormalPriority}转换后,每次使用优先级最高的work://转换后//是workList排序后,取优先级值最小的(值越小优先级越高)constcurWork=workList.sort((w1,w2)=>{returnw1.priority-w2.priority;})[0];改造后流程的变化可以从流程图看出。Scheduler不再直接执行perform,而是通过执行scheduleCallback来调度perform.bind(null,work)。即当满足一定的条件时,会产生一个新的任务:constsomeTask={callback:perform.bind(null,work),expiration:xxx}同时work的工作也可以中断。转换前,perform会同步执行work中的所有work:while(work.count){work.count--;insertItem();}转换后,work的执行过程随时可能被打断:while(!needYield()&&work.count){work.count--;insertItem();}高优先级打断低优先级实例constwork1={count:100,priority:LowPriority}遍历schedule(调度)并执行(执行)如下。当执行完80个作业时,突然插入了一个高优先级的作业。此时:constwork1={//work1work已经执行了80次,还有20次要执行。count:20,priority:LowPriority}//新插入的高优先级workconstwork2={count:100,priority:ImmediatePriority}work1工作中断,继续调度。由于work2的优先级较高,所以会进入work2对应的perform,执行100个job。work2执行完后,继续调度执行work1剩余的20个作业。在这个例子中,我们需要区分“中断”的两个概念:在第3步中,work1执行的工作被中断了。这是微观上的“中断”。由于work1被中断,调度继续进行。下一个要执行的作业是更高优先级的work2。work2的到来导致work1中断。这就是宏观上的“中断”。之所以区分“宏/微”,是因为“微中断”并不一定就是“宏中断”。例如:work1因为时间片用完而中断。如果没有其他更高质量的作品与他竞争档期,下一场演出仍将是作品1。这种情况下,微观上有多次中断,但在宏观上,仍然在执行同一个工作。这就是“时间分片”的原理。调度系统的实现原理以下是调度系统的完整实现原理:根据流程图:文章来自前端开发