requestIdleCallback
requestAnimationFrame的回调会在每一帧确定执行,属于高优先级任务,而requestIdleCallback的回调则不一定,属于低优先级任务。
我们所看到的网页,都是浏览器一帧一帧绘制出来的,通常认为FPS为60(每一秒刷新60次)的时候是比较流畅的,而FPS为个位数的时候就属于用户可以感知到的卡顿了,FPS60意味着:1000ms/60≈16.67,即每一帧的时间约为16.67ms(默认刷新率为60FPS)
那么在一帧里面浏览器都要做哪些事情呢,如下所示:
图中一帧包含了:处理用户的交互;JS解析执行;帧开始。窗口尺寸变更,页面滚去等的处理;rAF(即window.requestAnimationFrame);布局计算;页面重绘。
假如执行完这些操作用不了16.67ms,那么意味着这一帧有空闲时间,这段时间就恰好可以用来执行requestIdleCallback的回调,如下图所示:
当用户属于空闲状态(没有与网页进行任何交互),并且没有屏幕中也没有动画执行。此时空闲时间是无限长的。但是为了避免不可预测的事(用户突然和网页进行交互),空闲时间最大应该被限制在50ms以内。如下图所示:
上图可以看到当用户开始输入时,此时不再是空闲,但也要等该callback执行完才开始新的一帧,当然这时间间隔很短并不影响体验。
API
var handle = window.requestIdleCallback(callback[, options])
- callback: ():回调即空闲时需要执行的任务,接收一个 IdleDeadline 对象作为入参。其中 IdleDeadline 对象包含:
- didTimeout,布尔值,表示任务是否超时,结合 timeRemaining 使用。
- timeRemaining(),表示当前帧剩余的时间,也可理解为留给任务的时间还有多少。
- options:目前 options 只有一个参数
- timeout 。表示超过这个时间后,如果任务还没执行,则强制执行,不必等待空闲。
由于requestIdleCallback利用的是帧的空闲时间,所以就有可能出现浏览器一直处于繁忙状态,导致回调一直无法执行,此时需要传入参数timeout了,如果是因为timeout回调才得以执行的话,其实用户就有可能会感觉到卡顿了。
【example】
requestIdleCallback(myNonEssentialWork, {
timeout: 2000