浏览器 requestIdleCallback 方法
我们都知道,页面的内容都是一帧一帧绘制出来的,浏览器刷新率代表浏览器一秒绘制多少帧。原则上说 1s 内绘制的帧数也多,画面表现就也细腻。
目前浏览器大多是 60Hz(60 帧/s),每一帧耗时也就是在 16.6ms 左右。那么在这一帧的(16.6ms) 过程中浏览器又干了些什么呢?
浏览器一帧会经过下面这几个过程:
- 接受输入事件.(处理用户的交互,如点击、触碰、滚动等事件)
- 执行事件回调
- 开始一帧
- 执行 RAF (
RequestAnimationFrame) - 页面布局,样式计算
- 绘制渲染
- 执行 RIC (
RequestIdelCallback)
其实 RIC 并不是浏览器每帧都会去执行。而是在浏览器一帧(16.6ms)中做完了前面 6 件事儿且还有剩余时间,才会执行,如果没有剩余时间,那么就不会去执行了。
如果一帧执行结束后还有时间执行 RIC 事件,那么下一帧需要在事件执行结束才能继续渲染,所以 RIC 执行不要超过 30ms,如果长时间不将控制权交还给浏览器,会影响下一帧的渲染,导致页面出现卡顿和事件响应不及时
重点来了。我们怎么知道浏览器是否还有一帧剩余时间呢?
请看RequestIdelCallback 函数入参 deadline
requestIdleCallback((deadline) => {
// deadline 有两个参数
// timeRemaining(): 当前帧还剩下多少时间,最大值50ms
// didTimeout: 是否超时
// 另外 requestIdleCallback 后如果跟上第二个参数 {timeout: ...} 则会强制浏览器在当前帧执行完后执行。
if (deadline.timeRemaining() > 0) {
// TODO
} else {
requestIdleCallback(otherTasks);
}
});
// 用法示例
let tasksNum = 10000;
requestIdleCallback(unImportWork);
function unImportWork(deadline) {
while (deadline.timeRemaining() && tasksNum > 0) {
console.log(`执行了${10000 - tasksNum + 1}个任务`);
tasksNum--;
}
if (tasksNum > 0) {
// 在未来的帧中继续执行
requestIdleCallback(unImportWork);
}
}
这个API因浏览器版本兼容问题。Facebook 在 React 的重构升级中, 抛弃了 requestIdleCallback 的原生 API,而实现了功能更完备的 requestIdleCallbackpolyfill,这就是Scheduler。除了在空闲时触发回调的功能外,Scheduler 还提供了多种调度优先级供任务设置: