曾经有一个人,用非常怀疑及震惊的语气,问了我两遍:
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