You click in brothers and sisters, browser so dedicated to your service, why you still try to squeeze it, your heart will not feel guilty? !
Ok, just kidding, back to the point, our main characters today are requestAnimationFrame and requestIdleCallback. So what do they have to do with squeezing a browser? They allow us to drill down into the browser’s internal rendering life cycle, taking every moment we can and draining the browser.
requestAnimationFrame
Let’s start with requestAnimationFrame, which, as the name suggests, can be used for animations. Let’s consider a scenario in which a div slides to the right with JS. Our first reaction might be to use setInterval
<! 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>
<style>
#animation {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
</style>
</head>
<body>
<div id="animation" style="left: 10px"></div>
<script>
setInterval(() = > {
const div = document.querySelector('#animation');
const left = div.getBoundingClientRect().left;
div.style.left = `${left+2}px`;
}, 100);
</script>
</body>
</html>
Copy the code
So easy, let’s look at the effectIt actually looks okay, you know? But if you look at it, it’s a little bit unnatural, it’s kind of like dropping frames. Why is this? Because if it is 60 FPS, it will refresh 60 times within 1s, that is 1/60 seconds, but our time is set to 100ms, which is not consistent with the refresh time of the browser, so we have a feeling that the animation is not smooth enough. We can adjust the interval. But the best way is to let the browser control itself and let our function execute once per frame, because more execution is not necessary, less execution is not enough, one is just fine.requestAnimationFrame
That’s what it does. It takes a function that will only be called once in a frame, depending on the browser, so let’s change 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>
<style>
#animation {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
}
</style>
</head>
<body>
<div id="animation" style="left: 10px"></div>
<script>
// setInterval(() => {
// const div = document.querySelector('#animation');
// const left = div.getBoundingClientRect().left;
// div.style.left = `${left+2}px`;
// }, 100);
function animation() {
requestAnimationFrame(() = > {
const div = document.querySelector('#animation');
const left = div.getBoundingClientRect().left;
div.style.left = `${left+2}px`;
animation();
})
}
animation();
</script>
</body>
</html>
Copy the code
Since it will only be executed once at a time, we need to recursively call this function, and let’s see what happens
RequestAnimationFrame is a simple and useful way to use a setInterval.
requestIdleCallback
Now let’s talk about requestIdleCallback. What does it do? As we know, browser rendering is A set of processes, such as A, B, C, D four steps, A rendering is A->B->C->D, and then we can see the render out of the interface, want to know the specific content can be hidden in an article I wrote before. What if the browser takes 1/60th of a second to render 60 frames, and the job is done before it’s done? The browser said: Isn’t that just fishing? Will the capitalist give you an easy ride? No, every second has to be squeezed out. I can give you something to do, requestIdleCallback, with which we can give the browser more work to do in its spare time.
RequestIdleCallback takes a function that will be called when the browser is idle, and it also takes a parameter. For more details, refer to MDN, which I won’t go into here. What can you do with it? We know react updated the Fiber architecture. What’s that? In a nutshell, it used to be that if a component was too big and there were too many sub-components underneath it, it could only be rendered in one go. At this time, if other tasks come, they will be blocked, so some tasks can not be responded in time in the user’s view, that is, the interface is jammed, dropped frames.
React can interrupt updates to respond to higher-priority tasks. Here’s a quick example:
<! 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>
<script>
const taskList = [
{
id: 1.msg: 'first task'
},
{
id: 2.msg: 'second task'
},
{
id: 3.msg: 'third task'
},
{
id: 4.msg: 'four task'}]function wait(time) {
const now = Date.now();
while (Date.now() - now < time) {}
}
function oldExecuteTask(list = taskList) {
list.forEach(item= > {
console.log("execute task", item.msg);
wait(1000); })}function newExecuteTask(list = taskList) {
requestIdleCallback(() = > {
const firstList = list[0];
console.log(`execute task ${firstList.msg}`);
wait(1000);
list.length > 1 && newExecuteTask(list.slice(1));
}, { timeout: 2000})}</script>
</head>
<body>
<button onclick="oldExecuteTask()">execute old task</button>
<button onclick="newExecuteTask()">execute new task</button>
<button onclick="(() => {console.log('other task')})()">other task</button>
</body>
</html>
Copy the code
When executing the task queue, we click the Other Task button. At this time, the old React cannot respond because the previous task has not been completed, while the new React can respond because of the interrupt mechanism. Let’s look at the old effect first
As you can see, when there is no interruption, I frantically click the button and there is no console. When the task is finished, the previous console is displayed. With requestIdleCallback, we only perform one task at a time. The next task is called again via requestIdleCallback, ensuring that we can execute our task while responding to some other task, let’s see what happens
As you can see, we can execute tasks while responding to other tasks, which is similar to the react architecture. So, with requestIdleCallback, we can make better use of the fragmented time to perform low-priority tasks and not let the browser waste extra time.
conclusion
In fact, in my opinion, these two apis give us the opportunity to delve into the browser’s internal life cycle, which gives us a lot of room to squeeze the browser. Currently, the compatibility of requestIdleCallback is not very good, so students who want to use it in a real production environment need to research it carefully.