Hi everyone, I'm Kasong.
Everyone knows that React
has a scheduling system based on the Fiber
The basic functions of this scheduling system include:
- Updates have different priorities
- One update may involve
render
multiple components. Theserender
may be allocated to multiplemacro tasks for execution (that is,
time slice)
high priority update will interrupt the ongoing
low priority update
This article will implement this scheduling system with 100 lines of code, allowing you to quickly understand the scheduling principle of React
I know you don't like to look at large sections of code, so this article will explain the principle in the form of diagram +
code snippets.
At the end of the article, there is a complete online Demo, which you can play by yourself.
Open!
Welcome to join the human high-quality front-end framework group , take flight
Ready to work
We use work
represent a job, and work.count
represent the number of times that this job has to be repeated.
The Demo
to be repeated in 061baa4bbd84cc is "execute the insertItem
method and insert <span/>
into the page":
const insertItem = (content: string) => {
const ele = document.createElement('span');
ele.innerText = `${content}`;
contentBox.appendChild(ele);
};
So, for the following work
:
const work1 = {
count: 100
}
Representative: implementation of 100 insertItem
insert 100 to the page <span/>
.
work
can be comparedReact
onceupdate,
work.count
analogynumber of components of this update to render. So
Demo
is an analogy to the update process ofReact
To implement the first version of the scheduling system, the process is shown in the figure:
It includes three steps:
- Insert
work
into theworkList
queue (used to save allwork
schedule
method fromworkList
taken outwork
, passed toperform
perform
method after executingwork
repeated after all the workStep 2
code show as below:
// 保存所有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();
}
For button binding click interaction, the most basic scheduling system is completed:
button.onclick = () => {
workList.unshift({
count: 100
})
schedule();
}
Click button
to insert 100 <span/>
.
TheReact
is: clickbutton
to trigger a synchronous update, 100 componentsrender
Next we will transform it into asynchronous.
Scheduler
React
internally uses Scheduler complete asynchronous scheduling.
Scheduler
is a separate package. So we can use it to transform our Demo
.
Scheduler
presets 5 priority levels, and the priority levels are reduced from top to bottom:
ImmediatePriority
, the highest synchronization priorityUserBlockingPriority
NormalPriority
LowPriority
IdlePriority
, lowest priority
scheduleCallback
method receives the priority of and the callback function
fn
, which is used to schedule fn
:
// 将回调函数fn以LowPriority优先级调度
scheduleCallback(LowPriority, fn)
In Scheduler
inside performed scheduleCallback
generated after task
this data structure:
const task1 = {
expiration: startTime + timeout,
callback: fn
}
task1.expiration
representatives task1
expiration time, Scheduler
will give priority to the implementation of expired task.callback
.
expiration
in startTime
is the current start time, and timeout
with different priorities is different.
For example, ImmediatePriority
of timeout
is -1, because:
startTime - 1 < startTime
So ImmediatePriority
will expire immediately, and callback
immediately.
The IdlePriority
corresponding to timeout
is 1073741823 (the largest 31-bit signed integer), and its callback
takes a very long time to execute.
callback
will be executed in the new macro task, which is the principle of
Scheduler
Transform Demo with Scheduler
The process after transformation is shown in the figure:
Before the transformation, work
directly taken from the tail of the workList
// 改造前
const curWork = workList.pop();
After the transformation, work
can have a different priority of , which is
priority
field.
For example, the following work
represents insert 100 \<span/\> with NormalPriority priority:
const work1 = {
count: 100,
priority: NormalPriority
}
Therefore, after the transformation, the highest priority work
is used every time:
// 改造后
// 对workList排序后取priority值最小的(值越小,优先级越高)
const curWork = workList.sort((w1, w2) => {
return w1.priority - w2.priority;
})[0];
Process changes after transformation
Seen from the flowchart, Scheduler
no longer directly executed perform
, but by performing scheduleCallback
scheduling perform.bind(null, work)
.
That is, when certain conditions are met, a new task
generated:
const someTask = {
callback: perform.bind(null, work),
expiration: xxx
}
At the same time, work
is also interruptible. Before the transformation, perform
will synchronously execute all the work in work
while (work.count) {
work.count--;
insertItem();
}
After the transformation, work
may be interrupted at any time:
while (!needYield() && work.count) {
work.count--;
insertItem();
}
needYield
the implementation of the 061baa4bbd8ab6 method (when will it be interrupted) please refer to theonline Demo at the end of the article
High priority interrupt low priority example
For example, look at an example where high priority interrupts
low priority:
- Insert a low priority
work
, the attributes are as follows
const work1 = {
count: 100,
priority: LowPriority
}
- After experiencing
schedule
(scheduling),perform
(executing), when the job was executed 80 times, a high prioritywork
suddenly inserted. At this time:
const work1 = {
// work1已经执行了80次工作,还差20次执行完
count: 20,
priority: LowPriority
}
// 新插入的高优先级work
const work2 = {
count: 100,
priority: ImmediatePriority
}
work1
work is interrupted, continue toschedule
. Becausework2
higher priority, it will enterwork2
corresponding toperform
, and perform 100 jobswork2
executed, continue toschedule
and perform the remaining 20work1
In this example, we need to distinguish two concepts interrupting
- In step 3, the work performed by
work1
This is a microscopic view of interrupting - Because
work1
was interrupted, so continue toschedule
. The next performer is the more excellentwork2
.work2
soon lead towork1
be interrupted, this is a macro point of interrupted
The reason why macro/micro is distinguished is because micro-interrupting does not necessarily mean that macro-interrupting .
For example: work1
was interrupted due to exhaustion of time slices. If there is no other better work
to compete with him schedule
, the next time perform
is still work1
.
In this case, there are multiple interruptions at the micro level, but from a macro point of view, the same work
is still being executed. This is the principle of time slice
The realization principle of the dispatch system
The following is the complete implementation principle of the scheduling system:
Look at the flow chart:
Summarize
This article is React
scheduling system, which mainly includes two stages:
- schedule
- perform
Here is complete Demo address .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。