

你应该知道的requestIdleCallback
source link: https://gitlwz.github.io/2019/04/03/js-requestIdleCallback/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我们都知道React 16实现了新的调度策略(Fiber), 新的调度策略提到的异步、可中断,其实就是基于浏览器的 requestIdleCallback和requestAnimationFrame两个API。所以这里我们有必要了解一下这两个API。
什么是requestIdleCallback?
当关注用户体验,不希望因为一些不重要的任务(如统计上报)导致用户感觉到卡顿的话,就应该考虑使用requestIdleCallback。因为requestIdleCallback回调的执行的前提条件是当前浏览器处于空闲状态。
requestIdleCallback will schedule work when there is free time at the end of a frame, or when the user is inactive.
requestIdleCallback用法示例
1
2
3
4
5
6
7
8
9
10
11
12
13
requestIdelCallback(myNonEssentialWork);
function myNonEssentialWork (deadline) {
// deadline.timeRemaining()可以获取到当前帧剩余时间
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
doWorkIfNeeded();
}
if (tasks.length > 0){
requestIdleCallback(myNonEssentialWork);
}
}
requestIdleCallback和requestAnimationFrame有什么区别?
requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。
我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了,那么在一帧里面浏览器都要做哪些事情呢,如下所示:
图中一帧包含了用户的交互、js的执行、以及requestAnimationFrame的调用,布局计算以及页面的重绘等工作。
假如某一帧里面要执行的任务不多,在不到16ms(1000/60)的时间内就完成了上述任务的话,那么这一帧就会有一定的空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调,如下图所示:
由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,这其实也并不是我们期望的结果(如上报丢失),那么这种情况我们就需要在调用requestIdleCallback的时候传入第二个配置参数timeout了?
1
2
3
4
5
6
7
8
9
10
11
12
requestIdleCallback(myNonEssentialWork, { timeout: 2000 });
function myNonEssentialWork (deadline) {
// 当回调函数是由于超时才得以执行的话,deadline.didTimeout为true
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&
tasks.length > 0) {
doWorkIfNeeded();
}
if (tasks.length > 0) {
requestIdleCallback(myNonEssentialWork);
}
}
如果是因为timeout回调才得以执行的话,其实用户就有可能会感觉到卡顿了,因为一帧的执行时间必然已经超过16ms了
requestIdleCallback里面可以执行DOM修改操作吗?
强烈建议不要,从上面一帧的构成里面可以看到,requestIdleCallback回调的执行说明前面的工作(包括样式变更以及布局计算)都已完成。如果我们在callback里面做DOM修改的话,之前所做的布局计算都会失效,而且如果下一帧里有获取布局(如getBoundingClientRect、clientWidth)等操作的话,浏览器就不得不执行强制重排工作,这会极大的影响性能,另外由于修改dom操作的时间是不可预测的,因此很容易超出当前帧空闲时间的阈值,故而不推荐这么做。推荐的做法是在requestAnimationFrame里面做dom的修改,可以在requestIdleCallback里面构建Document Fragment,然后在下一帧的requestAnimationFrame里面应用Fragment。
除了不推荐DOM修改操作外,Promise的resolve(reject)操作也不建议放在里面,因为Promise的回调会在idle的回调执行完成后立刻执行,会拉长当前帧的耗时,所以不推荐。
推荐放在requestIdleCallback里面的应该是小块的(microTask)并且可预测时间的任务。关于microTask推荐看这里
requestIdleCallback的兼容情况
推荐使用npm包request-idle-callback
Recommend
-
133
我知道你不想跳槽,但你应该多去面试瞎说职场·2017-11-29 12:15面试也是一个自我提升的途径。编者按:本文转自微信公众号
-
80
页面不存在 - OSCHINA - 中文开源技术交流社区...
-
123
-
113
[译] 每个人都应该知道的十个机器学习常识
-
63
-
6
React Time Slice(三) - requestIdleCallback polyfillNovember 15, 2020/「 react 、time slice 」/
-
22
React Time Slice(二) - requestIdleCallback polyfillSeptember 16, 2020/「 react 、time slice 」/
-
9
React Time Slice(-) - requestIdleCallback polyfillSeptember 02, 2020/「 react 、time slice 」/
-
11
你不知道的 requestIdleCallback牧云云拼多多 前端工程师
-
19
Closed Bug 1676569 Opened 4 months ago Closed 16 days ago...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK