RequestIdleCallback () inserts a function that will be called when the browser is idle. 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.

API

const handle = window.requestIdleCallback(callback[, options])
Copy the code
  • callbackThe: callback, which is a task that needs to be performed when idle, accepts oneIdleRequestCallbackObject as a parameter.IdleRequestCallbackObjects include:
    • didTimeout: Boolean value that indicates whether the task times outtimeRemaininguse
    • timeRemaining: Indicates the remaining time of the current frame
  • options: Currently, only one parameter is available
    • timeout: indicates that the task is forcibly executed if the task has not been executed after the specified time

Example:

requestIdleCallback(myNonEssentialWork);

function myNonEssentialWork(deadline) {
  // deadline.timeremaining () retrieves the remaining time of the current frame
  while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    doWorkIfNeeded();
  }
  if (tasks.length > 0) { requestIdleCallback(myNonEssentialWork); }}Copy the code

Difference between requestIdleCallback and requestAnimationFrame

The requestAnimationFrame callback is confirmed every frame and is a high priority task. The callback to requestIdleCallback is not necessarily a low-priority task.

The page we see is drawn frame-by-frame by the browser, and FPS is usually smooth at 60, while FPS is slow at low FPS.

So what does the browser do in each frame, as shown below:

One frame includes user interaction, JavaScript script execution; RequestAnimationFrame (rAF) calls, layout calculations, page redrawing, etc.

If a frame does not perform a lot of tasks and the above tasks are completed in less than 16.66ms(1000/60), then the frame will have some free time to execute the requestIdleCallback callback, as shown in the figure below:

When the application stack is empty and the browser is idle, the time allowed for requestIdleCallback execution can be appropriately extended, up to 50ms, to prevent unpredictable tasks (such as user input) and avoid delays perceived by users when they cannot respond in a timely manner.

Since requestIdleCallback takes advantage of the frame’s idle time, it is possible that the browser is always busy and the callback cannot be executed. In this case, you need to pass the second configuration parameter timeout when calling requestIdleCallback.

requestIdleCallback(myNonEssentialWork, { timeout: 2000 });

function myNonEssentialWork(deadline) {
  Deadline. didTimeout is true if the callback is executed because of a timeout
  while (
    (deadline.timeRemaining() > 0 || deadline.didTimeout) &&
    tasks.length > 0
  ) {
    doWorkIfNeeded();
  }
  if (tasks.length > 0) { requestIdleCallback(myNonEssentialWork); }}Copy the code

If the timeout callback is executed, the user may feel stuck because a frame is longer than 16ms.

RequestIdleCallback performs DOM modification operations

It is strongly recommended not to perform DOM modification operations in requestIdleCallback.

As you can see from the composition of the above frame, the style changes, layout calculations, and so on are completed before the requestIdleCallback callback is executed. If you modify the DOM in the callback, the previous layout calculation is invalidated. And if there are operations related to retrieving the layout in the next frame, the browser will have to force a rearrangement, which can have a significant impact on performance. In addition, because the time to modify the DOM is unpredictable, it is easy to exceed the current frame free threshold.

The recommended approach is to make DOM changes in the requestAnimationFrame.

In addition to DOM modification not being recommended, Promise’s resolve(Reject) operation is also not recommended because Promise’s callback executes immediately after Idle’s callback completes, lengthening the current frame.

Example: Use requestIdleCallback to report data

In some cases, we want to be able to track an event, such as “click the navigation bar menu “. We will create an array of events to delay reporting and avoid sending events immediately.

var eventsToSend = [];

function onNavOpenClick() {
  // Animate the menu.
  menu.classList.add("open");

  // Store the event for later.
  eventsToSend.push({
    category: "button".action: "click".label: "nav".value: "open"}); schedulePendingEvents(); }Copy the code

Use requestIdleCallback to handle pending events:

function schedulePendingEvents() {
  // Only schedule the rIC if one has not already been set.
  if (isRequestIdleCallbackScheduled) return;

  isRequestIdleCallbackScheduled = true;

  if ("requestIdleCallback" in window) {
    // Wait at most two seconds before processing events.
    requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
  } else{ processPendingAnalyticsEvents(); }}Copy the code

The callback method executed by requestIdleCallback:

function processPendingAnalyticsEvents(deadline) {
  // Reset the boolean so future rICs can be set.
  isRequestIdleCallbackScheduled = false;

  // If there is no deadline, just run as long as necessary.
  // This will be the case if requestIdleCallback doesn’t exist.
  if (typeof deadline === "undefined")
    deadline = {
      timeRemaining: function () {
        return Number.MAX_VALUE; }};// Go for as long as there is time remaining and work to do.
  while (deadline.timeRemaining() > 0 && eventsToSend.length > 0) {
    var evt = eventsToSend.pop();

    ga("send"."event", evt.category, evt.action, evt.label, evt.value);
  }

  // Check if there are more events still to send.
  if (eventsToSend.length > 0) schedulePendingEvents();
}
Copy the code

reference

build your own react

You should know about requestIdleCallback

Using requestIdleCallback

MDN – requestIdleCallback