“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

I’ve already explained why browsers can stall when running time-consuming javascript code. The main reason for this is that javascript is single-threaded, and in the main thread you not only need to run javascript code but also do many other tasks, such as listening to user actions and rendering pages. For example, when the user scrolls through the list page, it sends a request to the remote server to obtain the data. When the user enters the text to be queried in the search text box, the user behavior does not respond immediately because the javascript code is executed, resulting in a poor user experience.

The code and sharing references the official documentation, but of course incorporates your own understanding of the FUNCTIONALITY and use of the API

As more and more complex logic is placed at the front end, the problem is always solved by the tips and tricks of the Web developer experience, which can seem overwhelming. To solve these problems at the API level, there is requestIdleCallback. Just like using requestAnimationFrame makes it easy to implement animations that play smoothly. The requestIdleCallback API allows us to use the idle time of each frame. This allows javascript to perform tasks in its spare time without interfering with higher-priority tasks, such as user interaction. This API has been available since Chrome 47, but the API is experimental and the specs are still in flux, so there may be changes in the future. It’s only supported by some browsers, and we need polyfill to make it available to more javascript developers running on other browsers.

I first encountered the requestIdleCallback when I was learning about the React fiber implementation. Fiber splits time-consuming tasks into smaller tasks and then, in the free time of the browser, performs those tasks.

Why use requestIdleCallback?

Being able to carry out non-essential tasks on your own is more difficult. Because after the requestAnimationFrame callback is executed, there are also the main threads of style calculations, layouts, screen drawing, and other browsers that need to run, it is impossible to calculate exactly how much time is left in that frame. So maybe what we want to do is we attach a listener to every way that the user interacts, such as scrolling, touching, or clicking, and the listener is just to determine whether the user is interacting with the interface. On top of that, we need to think about how to get the rest of the time, which sounds like a head-scratcher. RequestIdleCallback is an API that allows you to use any free time in the most efficient way. Let’s take a look at how to use requestIdleCallback

if ('requestIdleCallback' in window) {
    // Use requestIdleCallback to execute the task
    
  } else {
    // Implement the requestIdleCallback function yourself
  }
Copy the code

If the API is not supported in the browser, you can implement the API with setTimeout

window.requestIdleCallback =
  window.requestIdleCallback ||
  function (cb) {
    var start = Date.now();
    return setTimeout(function () {
      cb({
        didTimeout: false.timeRemaining: function () {
          return Math.max(0.50 - (Date.now() - start)); }}); },1);
  }

window.cancelIdleCallback =
  window.cancelIdleCallback ||
  function (id) {
    clearTimeout(id);
}

function aTask(deadline){
    console.log("a task")}window.requestIdleCallback(aTask)
Copy the code

Using setTimeout is not a good solution because setTime doesn’t know how much free time there is like requestIdleCallback, but if the browser doesn’t already support the requestIdleCallback API, call the function directly. So the way to do it is to temporarily hand over your task to a setTime to execute your task asynchronously and to temporarily smooth out some of the differences in support for this API between different browsers.

Using requestIdleCallback

Let’s see how to use the requestIdleCallback API, so by default, your browser supports requestIdleCallback. The call to requestIdleCallback is very similar to requestAnimationFrame, with the first argument being a callback function, and the code we want to execute at free time is written here.

requestIdleCallback(myNonEssentialWork);
Copy the code

When myNonEssentialWork is called, the callback function takes an object in which a method timeRemaining returns how much time is left in that frame.

function myNonEssentialWork (deadline) {
  while (deadline.timeRemaining() > 0)
    doWorkIfNeeded();
}
Copy the code
requestIdleCallback(myNonEssentialWork);

function myNonEssentialWork (deadline) {
    while (deadline.timeRemaining() > 0)
      doWorkIfNeeded();
}

function doWorkIfNeeded(){
    console.log("do work if needed...")}Copy the code

You can call the timeRemaining function to return the remaining time each time. When timeRemaining() returns 0, if you have more tasks to do, the task can be executed through another requestIdleCallback.

requestIdleCallback(myNonEssentialWork);



const tasks = [
    () = >{
        console.log("task one");
    },
    () = >{
        console.log("task two");
    },
    () = >{
        console.log("task three"); }]function myNonEssentialWork (deadline) {
    while (deadline.timeRemaining() > 0 && tasks.length > 0)
        doWorkIfNeeded();

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

function doWorkIfNeeded(){
    tasks.shift()()
}
Copy the code

How do I ensure that the callback function is executed

Consider that if the main thread of the browser is always busy, with no idle time, then the callback function might never be executed. Although requestIdleCallback is similar to requestAnimationFrame, they differ in that the requestIdleCallback process accepts a callback function as the first argument and provides an optional second argument. Allows the user to set the execution time of the timeout (in milliseconds). If this timeout period is set, when it is exceeded, the browser will actively perform the callback regardless of whether there is idle time.

/ / will take the initiative to perform processPendingAnalyticsEvents wait for 2 seconds
requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
Copy the code

Once the callback is executed because the timeout is triggered, two things are noticed:

  • TimeRemaining () returns 0
  • The didTimeout property of the deadline object will be True. When didTimeout is True, it indicates that it has timed out, which is also possible to run the task you want to run
function myNonEssentialWork (deadline) {

 / / conditions when the have a rest or already timeout, deadline. DidTimeout through this value to determine whether a timeout, if the timeout even without rest will also perform this task
  while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
         tasks.length > 0)
    doWorkIfNeeded();

  if (tasks.length > 0)
    requestIdleCallback(myNonEssentialWork);
}
Copy the code

Because the timeout period is set, when the timeout period is exceeded, the callback function will be actively executed, which will return to the above problem and may affect the user experience, so think twice before setting this parameter. If possible, the browser decides when to call the callback.

RequestIdleCallback is used to send data to the server

Flask: Flask: flask: Flask: Flask: Flask: Flask: Flask: flask: flask: flask

from flask import Flask
from flask import Flask, request
from flask_restful import Resource, Api
from flask_cors import CORS

import time

app = Flask(__name__)
cors = CORS(app)
api = Api(app)

tasks = {
    "one":"task one"."two":"task two"."three":"task three",}class TodoSimple(Resource) :
    def get(self, task) :
        time.sleep(1)
        return {task: tasks[task]}

   

api.add_resource(TodoSimple, '/<string:task>')


if __name__ == '__main__':
    app.run(host='0.0.0.0',port=5000,debug=True)
Copy the code

Let’s look at an example of how to use requestIdleCallback to send analysis data. In this case, we might want to track an event, say, by clicking on a navigation menu. However, since they usually appear as animations on the screen, we will want to avoid sending the event to Google Analytics immediately. We will create an array of events to send and ask them to be sent at some point in the future.


var isRequestIdleCallbackScheduled  = false;

var eventsToSend = [];

// To simulate firing a request event and then sending the event to the eventsToSend array
function triggerCollectIamge (name) {
  eventsToSend.push(
    {
      task:name
    });

  schedulePendingEvents();
}

triggerCollectIamge("one");
triggerCollectIamge("one");
triggerCollectIamge("two");
triggerCollectIamge("one");
triggerCollectIamge("one");
triggerCollectIamge("three");
triggerCollectIamge("two");
triggerCollectIamge("two");

// Do the network time-consuming work
function doSomeTask(name){
    fetch("http://10.1.0.67:5000/"+name)
    .then(function(response) {
        return response.json();
      })
      .then(function(myJson) {
        console.log(myJson);
      });
}

function schedulePendingEvents() {

    
    if (isRequestIdleCallbackScheduled)
      return;
  
    isRequestIdleCallbackScheduled = true;
  
    if ('requestIdleCallback' in window) {
      // Set the timeout event to 2 seconds
      requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
    } else{ processPendingAnalyticsEvents(); }}function processPendingAnalyticsEvents (deadline) {

   
    // Reset this parameter
    isRequestIdleCallbackScheduled = false;
  
  
    // If there is no deadline object, create one manually and return enough time left
    if (typeof deadline === 'undefined')
      deadline = { timeRemaining: function () { return Number.MAX_VALUE } };
  
    
    // Execute as long as there is free time and unfinished tasks
    while (deadline.timeRemaining() > 0 && eventsToSend.length > 0) {
      var evt = eventsToSend.pop();
      doSomeTask(evt.task);
    }
  
    SchedulePendingEvents (); // schedulePendingEvents ()
    if (eventsToSend.length > 0)
      schedulePendingEvents();
  }
Copy the code
<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
    <button onclick="console.log('clicking one btn')">click</button>
    <! -- <script src="index.js"></script> -->
    <script src="requestIdelCB_demo.js"></script>
</body>
</html>
Copy the code

Problems with DOM updates using requestIdleCallback and solutions

Let’s talk about another scenario where requestIdleCallback can really help performance, when updating the DOM isn’t a high priority, such as adding an item to the end of a growing, lazy list. At this moment requestIdleCallback.

Browsers can be too busy to run any callback in a frame, so you shouldn’t expect to have enough free time at the end of a frame to do any more tasks. That’s a little different than setImmediate, who runs in frames.

Since the tasks in the callback are not executed until the end of each frame, this means that the style changes, layout calculations, and so on have been completed. If a DOM modification is made in the idle callback, these layout calculations are invalid. In the next frame, to get the layout in some form, such as getBoundingClientRect, clientWidth, etc., the browser will have to perform a forced synchronization of the layout, a potential performance bottleneck.

Another reason not to do anything to the DOM in the idle callback is that the time impact of manipulating the DOM is unpredictable and can easily exceed the deadline provided by the browser.

A good solution is to do DOM updates only in the requestAnimationFrame callback, because this type of task is allocated with the browser in mind. This means that our code will need to use a document fragment, which we can then append in the next requestAnimationFrame callback. If you are using the VDOM library, requestIdleCallback will be used to update the VDOM, and the next requestAnimationFrame callback will update the corresponding DOM, instead of the IDLE callback to the VDOM.