preface
React 16 uses Fiber instead of synchronous VDOM rendering to improve page rendering performance and user experience. There are plenty of good tech secrets out there. Here’s how React Fiber works.
A single task
In the early single-task system, users can only submit one task at a time. The current running task has all hardware and software resources. If the task does not release the CPU control actively, it will occupy all resources, which may affect other tasks and cause resource waste. This mode is very similar to the current browser operation mode. Because the UI thread and JS thread are mutually exclusive, once JS is executed for a long time, the browser cannot respond to user interaction in a timely manner, which may cause interface lag. In the early React synchronous rendering mechanism, when too many nodes are updated at one time, user experience is affected.
interrupt
Interrupts were originally used as a means to improve processor efficiency. In the absence of interrupts, when the CPU is executing a piece of code, if the program does not actively exit (such as an infinite loop code), then the CPU will be occupied, affecting the execution of other tasks.
while(true) {
...
};
Copy the code
The interrupt mechanism forces the interruption of code executed by the current CPU to execute previously registered interrupt service routines instead. More common such as: clock interrupt, it every certain time will interrupt the current task being executed, and immediately execute the pre-set interrupt service procedures, so as to realize the alternate execution between different tasks, which is also an important basic mechanism in the multi-task system. The interrupt mechanism is triggered mainly by hardware, and the CPU is passively accepted. With interrupts, each task execution time can be very well controlled.
Back to the browser, most of the current browser is 60 hz (60 frames/second), so each frame takes about 16ms, it will go through the following processes:
- Input event handling
- requestAnimationFrame
- DOM rendering
- RIC (RequestIdleCallback)
We cannot intervene except for padding in steps 1-3, and RIC in Step 4 is a mechanism to prevent the waste of redundant computing resources. For example, when steps 1-3 only consume 6ms in a frame, the remaining 10ms of computing resources will be wasted. RIC is a resource utilization interface provided by browsers. RIC is very much like the “interrupt service” mentioned earlier, and each frame of the browser is like the “interrupt mechanism”, which can be used to implement the big task lag we mentioned earlier, for example, when we write the following code in JS, we will definitely block the browser rendering.
function task(){
while(true){
...
};
}
task();
Copy the code
With RIC, however, we can make large tasks perform periodically without preventing the browser from rendering properly.
Adjust the above sample code to RequestIdleCallback as follows:
function task(){
while(true){
...
};
}
requestIdleCallback(task);
Copy the code
Unfortunately, since our code is running in user mode, we can’t sense the real interrupt at the bottom, and the RIC we are using is only an approximate simulation of the interrupt. The above code will not be forced to interrupt after 16ms. We can only release it and return control to the browser. RIC provides timeRemaining methods to let tasks know when to proactively release the elaining. We adjust the above code as follows:
function task(deadline){
while(true){
...
if(! deadline.timeRemaining()) { requestIdleCallback(task);// Actively exit the loop and return control to the browser
break; }}; } requestIdleCallback(task);Copy the code
The above example allows a large loop to be “broken” without blocking the browser’s rendering and response.
Note: RIC calls are made about 20 times per second, well below page fluency requirements! The React Fiber is based on a set of custom mechanisms to simulate the implementation, such as: SetTimeout, setImmediate, MessageChannel
Here is the active release snippet in React Fiber:
function workLoop(hasTimeRemaining, initialTime) {
let currentTime = initialTime;
advanceTimers(currentTime);
currentTask = peek(taskQueue);
while( currentTask ! = =null &&
!(enableSchedulerDebugging && isSchedulerPaused)
) {
if( currentTask.expirationTime > currentTime && (! hasTimeRemaining || shouldYieldToHost()) ) {// If it times out, it actively exits the loop and returns control to the browser
break; }... }... }Copy the code
Scheduling tasks
With the interrupt mechanism, after the interruption of service, different tasks can realize the possibility of intermittent execution. How to realize the reasonable scheduling of multiple tasks requires a scheduling task to deal with, which usually represents the operating system. For example, when task A is interrupted by the interrupt mechanism, the operating system needs to protect the current task A on site, such as register data, and then switch to the next task B. When task A is scheduled again, the operating system needs to restore the previous task A on site, such as: Register data to ensure that task A can continue to execute the next half of the task. It is a very important function to ensure that the information of interrupted tasks is not destroyed in the scheduling process.
The RIC mechanism provided by the browser is similar to the “interrupt service” registration mechanism. After registration, we can realize the “interrupt” effect as long as we release it at an appropriate time. It has just been mentioned that for switching between different tasks, after interruption, we need to consider on-site protection and on-site restoration. Early React was a synchronous rendering mechanism, which was actually a recursive process. Recursion might lead to a long call stack, which actually complicated field protection and restoration. React Fiber split the recursive process into a series of small tasks and converted it into a linear linked list structure. In this case, onsite protection only needs to save the structure information of the next task. Therefore, additional information needs to be added to the split task. The structure records the essential information required for task execution:
{
stateNode,
child,
return,
sibling,
expirationTime
...
}
Copy the code
Let’s look at the following example code:
ReactDOM.render(
<div id="A">
A
<div id="B">
B<div id="C">C</div>
</div>
<div id="D">D</div>
</div>,
node
);
Copy the code
When React renders, the following task chain will be generated. If time is insufficient after task B is executed, only the information of the next task C needs to be recorded and the information of the last task can be obtained when task C is scheduled again. With this mechanism, priority, undo, suspend, and resume of rendering tasks are very well controlled.
conclusion
Interrupt mechanism is actually a very important means to solve the resource sharing, for the operating system, it has become an essential function. As the functions of the browser become more and more powerful, more and more functions are transferred to the browser. How to ensure the user’s fluency in the process of using the browser is also a problem that needs to be considered frequently. In the process of business development, we can make good use of the “interrupt mechanism” according to the actual situation to improve user experience.