requestAnimationFrame 和 requestIdleCallback

2,689 阅读2分钟

曾经有一个人,用非常怀疑及震惊的语气,问了我两遍:requestAnimationFrame是不是每帧必执行?? 答案是当然的啦!啦!!

概念

  • requestAnimationFrame: 告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵dom,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒60次。
  • requestIdleCallback: 会在浏览器空闲时间执行回调,也就是允许开发人员在主事件循环中执行低优先级任务,而不影响一些延迟关键事件。如果有多个回调,会按照先进先出原则执行,但是当传入了timeout,为了避免超时,有可能会打乱这个顺序。

requestAnimationFrame用法:

window.requestAnimationFrame(callback)

  • 参数: 其中callback接受一个参数DOMHighResTimeStamp参数,表示该callback执行的时间。

  • 返回值: 返回值requestID:是一个long integer 类型的值。 可以使用window.cancelAnimationFrame(requestID)来取消这个回调。


requestIdleCallback用法:

var requestID = window.requestIdleCallback(callback[, options])

  • options: 是一个对象,现在只有一个属性被定义:timeout 表示该回调超过这个时间扔未执行,就会强制执行,不必等空闲时间,这个请看下面代码示例

  • callback参数: 是IdleDeadline对象,包含两个属性: 1:didTimeout是一个只读布尔值,表示是否超时。

    2: timeRemaining(),是一个方法,返回当前帧还剩多少空余时间。如果没有时间就返回0。

  • 返回值: 返回值requestID可以使用window.cancelIdleCallback(requestID)来取消这个回调。


二者用途

requestAnimationFrame:操作DOM,动画

requestIdleCallback: 低优先级任务;因为它发生在一帧的最后,此时页面布局已经完成,所以不建议在 requestIdleCallback 里再操作 DOM,这样会导致页面再次重绘。

代码示例

// 一个sleep函数,模拟阻塞
function sleep(d) {
    for (var t = Date.now(); Date.now() - t <= d;);
}
let count = 0;
function callself(){
    console.log(++count, 'frame')
    sleep(16)
    if(count<20){
        window.requestAnimationFrame(callself);
    }
}
// 当count<20时候,就一直使用raf占满16ms,这样模拟一帧中无空闲时间
window.requestAnimationFrame(callself);

function cb1({didTimeout}){
    console.log('idle cb1', didTimeout)
}
function cb2({didTimeout}){
    console.log('idle cb2', didTimeout)
}
function cb3({didTimeout}){
    console.log('idle cb3', didTimeout)
}

// 注册三个rIC回调,正常是按照先进先出原则执行这三个回调,当设置的有timeout,该回调会被提前
window.requestIdleCallback(cb1)
window.requestIdleCallback(cb2)
window.requestIdleCallback(cb3, {
    timeout: 30
})

执行结果:

1 "frame"
idle cb3 true // cb3被提前了,因为设置了timeout,因此未等到帧空闲就执行了
2 "frame"
3 "frame"
4 "frame"
5 "frame"
6 "frame"
7 "frame"
8 "frame"
9 "frame"
10 "frame"
11 "frame"
12 "frame"
13 "frame"
14 "frame"
15 "frame"
16 "frame"
17 "frame"
18 "frame"
19 "frame"
20 "frame"
idle cb1 false // 一直等到帧空闲了才开始执行,并且cb1、cb2是按照顺序执行的
idle cb2 false