This post is simultaneously posted on my Github blog

preface

RequestIdleCallback was briefly mentioned in the last article on the understanding of React Fiber. The React source code has polyfilled this method. Understanding it also gives you a better understanding of Fiber. This article takes a closer look at this approach.

What is requestIdleCallback

RequestIdleCallback is a method on the window property that performs low-priority tasks during the remaining free time of a browser frame.

Why requestIdleCallback

In running a web page, there are many time-consuming tasks that are not that important. These tasks share event queues with important tasks such as those that respond promptly to user input. If the two conflict, the user experience will be terrible.

RequestIdleCallback solves this pain point by performing a callback at the end of each frame with free time.

Assume that a large number of calculations involving DOM operations are needed, during which the browser may have obvious lag behaviors and even cannot carry out any operations. Because it is a single JS thread, even if it is used for input processing, after a given frame is rendered and synthesized, the user’s main thread will become idle until the beginning of the next frame.

This free time can be used for low-priority tasks. React16’s scheduling strategy is asynchronously interruptible, and the key is the polyfill method. React maximizes rendering performance by subdividing tasks (time slices) and executing them when the browser is idle.

The essence of time slicing is to simulate the implementation of requestIdleCallback

At this point, going from React15 to React16 Fiber is a big improvement in overall performance; But remember, the optimization made by Act16 does not significantly reduce the number of tasks. The total amount of code you write does not change. You just use the free time to work, and you can get the work done faster. That’s just one Angle. React also does priority enforcement and so on.

Screen refresh rate versus FPS

The current refresh rate of most screens is 60 Hz, 60 frames per second (FPS: 60), that is, the screen refreshes 60 times per second. At this time, the time of a frame is 16.7ms (1000ms/60). If the time of a frame is lower than 60 Hz, the human eye will perceive the situation such as frame lag.

A browser frame says a complete redraw.

Front-end browsers refer to the number of Frames Per Second, or the number of refreshes Per Second. In theory, the higher the FPS, the smoother the interface.

Window. RequestIdleCallback () usage

Window. RequestIdleCallback () method will be called function in browser free time line. This enables developers to perform background and low-priority work on the main event loop without affecting the delay of critical events such as animations and input responses. Functions are typically executed in first-come-first-called order; however, if a callback function specifies a timeout, it is possible to scramble the order of execution in order to execute the function before it times out.

You can call requestIdleCallback() in the idle callback function to schedule another callback before passing through the event loop the next time.

var handle = window.requestIdleCallback(callback[, options])
Copy the code

Returns an ID identifier.

Callback: A reference to a function to be called when the event loop is idle. The function receives an argument called Deadline, which gets the status of the current idle time and whether the callback has been executed before the timeout. There are two attributes on this object: -timeremaining: The timeRemaining attribute is a function whose return value represents the remaining time of the current idle time. -didTimeout: The didTimeout attribute is a Boolean value. If didTimeout is true, this callback is executed because of a timeout. Options optional includes optional configuration parameters. Has the following properties: timeout: If timeout is specified with a positive value and the callback has not been invoked by timeout milliseconds, the callback will be enforced in the next idle period, although this is likely to have a negative impact on performance.Copy the code
function myNonEssentialWork(deadline) {
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
    doWorkIfNeeded()
  }

  if (tasks.length > 0) {
    requestIdleCallback(myNonEssentialWork)
  }
}

requestIdleCallback(myNonEssentialWork, 5000)
Copy the code

Let’s look at a practical example:

requestIdleCallback(myWork)

// A task queue
let tasks = [
  function t1() {
    console.log('Execute Task 1')},function t2() {
    console.log('Task 2')},function t3() {
    console.log('Task 3')},]// Deadline is an object returned by requestIdleCallback
function myWork(deadline) {
  console.log('Remaining time of current frame:${deadline.timeRemaining()}`)
  // Check whether the remaining time of the current frame is greater than 0 && if there are any remaining tasks
  if (deadline.timeRemaining() > 0 && tasks.length) {
    // Do something here
    const task = tasks.shift()
    task()
  }
  // If there are still tasks that have not been executed, it is put into the next frame schedule to continue execution, similar to recursion
  if (tasks.length) {
    requestIdleCallback(myWork)
  }
}
Copy the code

My results are as follows (each run varies from machine to machine) :

Remaining Time in current Frame: 15.120000000000001 Performing Task 1 Remaining time in current frame: 15.445000000000002 Performing Task 2 Remaining time in current frame: 15.21 Performing Task 3Copy the code

If the timeout callback is executed, the user might feel stuck, because the execution time of a frame must have exceeded 16ms

Defects in the requestIdleCallback method

This approach works in theory, but why did the React team polyfill it?

  1. Browser compatibility issues

  2. The requestIdleCallback’s FPS is only 20, which is a 50ms refresh, far below the page fluency requirement, so the React team had to implement it themselves.

Note: The maximum timeRemaining is 50ms, which is based on research. In other words, the response to user input in less than 100 milliseconds is considered instantaneous and will not be detected. Limiting idle time to 50ms means that even if a user action occurs immediately after the idle task starts, the user agent still has a remaining 50ms in which to respond to user input without perceptible lag to the user.

RequestIdleCallback differs from requestAnimationFrame

  • The requestAnimationFrame callback is determined to execute on each frame and is a high priority task
  • The callback of requestIdleCallback is not necessarily executed only when there is free time. It is a low-priority task.

Tell the browser window. RequestAnimationFrame () – you want to perform an animation, and required the browser until the next redraw calls the specified callback function to update the animation. This method takes as an argument a callback function that is executed before the browser’s next redraw

RequestAnimationFrame has two main advantages over setTimeout and setInterval:

  • RequestAnimationFrame brings together all DOM operations in each frame in a single redraw or reflow that closely tracks the browser refresh rate, which is typically 60 frames per second.

  • RequestAnimationFrame will not be redrawn or reflow in hidden or invisible elements, which of course means less CPU, GPU, and memory usage.

Analog implementation of requestIdleCallback

SetTimeout simulation

Of course, setTimeout is usually not used, because the error is very large

window.requestIdleCallback = function (handler) {
  let startTime = Date.now()

  return setTimeout(function () {
    handler({
      didTimeout: false.timeRemaining: function () {
        return Math.max(0.50.0 - (Date.now() - startTime))
      },
    })
  }, 1)}Copy the code

The React team uses requestAnimationFrame and postMessage to simulate the React process. The React team uses requestAnimationFrame and postMessage to simulate the React process.

reference

  • Familiar with requesTidlecallback to understand the React Ric Polyfill implementation
  • React requestIdleCallback implementation principle


Open source projects I will be maintaining in the near future:

  • Use React + TypeScript + Dumi + Jest + Enzyme to develop UI component libraries
  • Next. Js Enterprise project scaffold template
  • If you feel good, welcome star, give me a little encouragement to continue writing ~