The difference between requestIdleCallback and requestAnimationFrame

Page smoothness and FPS

The page is drawn frame by frame. When the number of frames per second (FPS) reaches 60, the page is smooth. When it is less than this value, the user will feel stuck.

1s has 60 frames, so the time allocated to each frame is 1000/60 ≈ 16 ms. So when we write code, we strive not to let the workload of one frame exceed 16ms.

Frame

So what does the browser need to do every frame?

image

As you can see from the picture above, the following six steps need to be completed within one frame:

  • Handle user interactions
  • JS parsing execution
  • Frame starts. Processing of window size changes, page scrolling, etc.
  • requestAnimationFrame(rAF)
  • layout
  • draw

requestAnimationFrame

When there is no requestAnimationFrame method to perform animation, we may use setTimeout or setInterval to trigger visual changes; but the problem with this approach is: The execution time of the callback function is not fixed. It may be right at the end, or it may not be executed directly. This often causes frame loss and page freezes.

image

In the final analysis, the reason for the above problem lies in timing, that is, the browser needs to know when to respond to the callback function. setTimeout or setInterval uses a timer to trigger the callback function. However, the timer cannot guarantee accurate execution. There are many factors that will affect its operation. Timing, for example: when synchronous code is executed, it will wait until the synchronous code is completed and there are no other tasks in the asynchronous queue before it is its turn to execute. Moreover, we know that the optimal time for each re-rendering is approximately 16.6 ms. If the time interval of the timer is too short, it will cause over-rendering and increase overhead; if it is too long, it will delay rendering and make the animation unsmooth.

The requestAnimationFrame method is different from setTimeout or setInterval. It depends on the system to determine the execution timing of the callback function and will request the browser to re-render next time. before executing the callback function. No matter what the refresh rate of the device is, the time interval of requestAnimationFrame will closely follow the time required for the screen to refresh once; for example, the refresh rate of a certain device is 75 Hz, then this The time interval is 13.3 ms (1 second/75 times). It should be noted that although this method can guarantee that the callback function is only rendered once in each frame, if too many tasks are executed in this frame, it will still cause lag; therefore, it The only guarantee is that the minimum re-rendering interval is the screen refresh time.

The specific description of the requestAnimationFrame method can be found in the relevant documentation of MDN. Let’s learn how to use it through an example of web animation.

let offsetTop = 0;
const div = document.querySelector(".div");
const run = () => {
 div.style.transform = `translate3d(0, ${offsetTop + = 10}px, 0)`;
 window.requestAnimationFrame(run);
};
run();

If you want to achieve animation effects, you must call the requestAnimationFrame method again every time you execute the callback function; it is the same as setTimeout to achieve animation effects, but no settings are required. time interval.

requestIdleCallback

It does not take more than 16 ms after the above six steps are completed, indicating that there is enough time, and the task registered in requestIdleCallback will be executed at this time.

image

It can also be seen from the above figure that, unlike requestAnimationFrame, each frame must be executed differently. requestIdleCallback picks up the browser’s idle time to perform tasks.

As a result, if the browser has been in a very busy state, the task registered by requestIdleCallback may never be executed. At this time, you can set timeout (see API introduction below) to ensure execution.

API
var handle = window.requestIdleCallback(callback[, options])
  • callback: Callback, which is a task that needs to be performed when idle. The callback function receives an IdleDeadline object as an input parameter. The IdleDeadline object contains:
    • didTimeout, Boolean value, indicates whether the task times out, used in conjunction with timeRemaining.
    • timeRemaining() indicates the remaining time of the current frame, which can also be understood as how much time is left for the task.
  • options: Currently options has only one parameter
    • timeout. It means that after this time, if the task has not been executed, it will be forcibly executed without waiting for idle time.

IdleDeadline object reference MDN:developer.mozilla.org/zh-CN/docs/…

Example
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
?
//task queue
const tasks = [ () => { console.log("First task"); }, () => { console.log("Second task"); }, () => { console.log ("The third task"); },];
?
function myNonEssentialWork (deadline) {
 // If there is extra time in the frame, or timeout
 while ((deadline.timeRemaining() > 0 || deadline.didTimeout) & amp; & amp; tasks.length > 0) {
   work();
 }
?
 if (tasks. length > 0)
   requestIdleCallback(myNonEssentialWork);
 }
?
function work () {
 tasks.shift()();
 console.log('Execute task');
}
 

In the case of timeout, it actually means that the browser is very busy and has no free time. At this time, it will wait for the specified timeout before executing. The deline >didTmieout will be true, and timeRemaining () will also return 0. If you choose to continue execution after a timeout, there will definitely be lags, because the time of one frame will inevitably be lengthened.

cancelIdleCallback

Similar to setTimeout, returns a unique id and can cancel the task through cancelIdleCallback.

Summary

Some low-priority tasks can be executed using requestIdleCallback when the browser is not busy. At the same time, because time is limited, the tasks it performs should be micro tasks that can be quantified and subdivided as much as possible. ).

Because it occurs at the end of a frame, when the page layout has been completed,it is not recommended to operate the DOM in requestIdleCallback, as this will cause the page to be redrawn again. DOM manipulation is recommended in rAF. At the same time, the time required to operate the DOM is uncertain, because it will cause recalculation of layout and view drawing, so such operations are not predictable.

Promise is not recommended here, because the callback attribute of Promise, a micro-task with a higher priority in the Event loop, will be executed immediately when requestIdleCallback ends, regardless of whether it is still available at this time. There is extra time, so it is very likely that a frame will exceed 16 ms.