该api告诉浏览器,希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
执行时机是每一帧浏览器进行渲染之前,是一个高优的任务。
当你准备更新动画时你应该调用此方法。这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数 (即你的回调函数)。
回调函数执行次数通常是每秒 60 次,但在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。为了提高性能和电池寿命,因此在大多数浏览器里,当requestAnimationFrame()
运行在后台标签页或者隐藏的 里时,
requestAnimationFrame()
会被暂停调用以提升性能和电池寿命。
也就是说,当我们切换浏览器的tab页面,或者浏览器窗口最小化以后,使用改api模拟的动画会暂停,直到当前页面再次出现在屏幕的可视区域。
回调函数会被传入DOMHighResTimeStamp
参数,DOMHighResTimeStamp
指示当前被 requestAnimationFrame()
排序的回调函数被触发的时间。在同一个帧中的多个回调函数,它们每一个都会接受到一个相同的时间戳,即使在计算上一个回调函数的工作负载期间已经消耗了一些时间。该时间戳是一个十进制数,单位毫秒,最小精度为 1ms(1000μs)。
通俗的说,该api会给传递的回调函数一个参数,该参数是当前页面渲染完成到当前函数执行时,已经过去了多少时间。这个参数的值在后续的每次渲染都会不断增加,因为浏览器渲染完毕以后,时间总是在增加。当然呢刷新页面以后会被重置。
Document
我们在回调函数中,再把当前的动画回调函数放入requestAnimationFrame中,会在下一次渲染页面前继续执行该动画函数
window.requestIdleCallback()
方法插入一个函数,这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout
,则有可能为了在超时前执行函数而打乱执行顺序。
在浏览器每次渲染页面时,我们会反馈(响应用户的输入),页面的拖拽,浏览器大小的改变,js主线程的任务,微任务宏任务,requestAnimationFrame回调,浏览器进行绘制,如果这些事情做完以后,还有剩余时间,我们认为这就是当前渲染帧的空闲时间,这个时间浏览器可以交给我们开发者(js主线程执行一些优先级不高的任务)。
可以这样认为,我们使用这个api了,那么浏览器在当前帧还有剩余时间,那么就会把执行权交给我们,但是我们也需要在剩余时间结束之前把执行权还给浏览器。
要记住,拿到执行权,然后在使用完毕以后,把执行权还给浏览器,这个是我们开发者和浏览器的 “君子协议”,你当然拿到执行权可以不交给浏览器了(比如我们进行一次死循环,那么很显然浏览器会卡死,没办法响应用户输入等)。
const sleep = (time) => {const now = Date . now()while (Date . now() < now + time) { }}const worksQueue = [() => {console . log('任务A开始')sleep(20)console . log('任务A结束')},() => {console . log('任务B开始')sleep(20)console . log('任务B结束')},() => {console . log('任务C开始')sleep(20)console . log('任务C结束')},() => {console . log('任务D开始')sleep(20)console . log('任务D结束')},() => {console . log('任务E开始')sleep(20)console . log('任务E结束')}]const doSomething = (idleDeadline) => { // idleDeadline.timeRemaining() // 返回值是剩余时间 while (idleDeadline. timeRemaining() > 0 && worksQueue.length) {console . log(idleDeadline. timeRemaining())const work = worksQueue. shift()work()}// 还有任务 if(worksQueue.length) requestIdleCallback(doSomething)}requestIdleCallback(doSomething)
看打印结果,可以发现:
我们可以看出来,前面的剩余时间都很正常,但是中间出现了一个50,这个是为什么?
因为浏览器可以发现,我们页面已经长时间没有发生过交互了,而对于人眼来说,响应交互的时延小于在50ms,也就是一秒20帧,人不会感觉明显的卡顿,所以浏览器会多给我们一些时间来执行这些优先级不高的任务。
如果我们把任务执行时间延长,比如一个任务需要1000ms,会发生什么?
const worksQueue = [() => {console . log('任务A开始')sleep(20)console . log('任务A结束')},() => {console . log('任务B开始')sleep(1000)console . log('任务B结束')},() => {console . log('任务C开始')sleep(20)console . log('任务C结束')},() => {console . log('任务D开始')sleep(1000)console . log('任务D结束')},() => {console . log('任务E开始')sleep(20)console . log('任务E结束')}]
很明显,浏览器都会提醒我们,执行的时候时间超时了。
但是,因为这个任务是非常低优先级的,可能任务在多次渲染后,仍没有机会很执行。
所以我们还可以传递一个参数,timeout指定超时时间,如果超时了(太长时间还没执行该任务),那么我们也去执行这些低优先级的任务。
如果didTimeout为ture,表明当前任务正在执行,且上次因为超时没有执行该任务
const sleep = (time) => {const now = Date . now()while (Date . now() < now + time) { }}const worksQueue = [() => {console . log('任务A开始')sleep(20)console . log('任务A结束')},() => {console . log('任务B开始')sleep(20)console . log('任务B结束')},() => {console . log('任务C开始')sleep(20)console . log('任务C结束')},() => {console . log('任务D开始')sleep(20)console . log('任务D结束')},() => {console . log('任务E开始')sleep(20)console . log('任务E结束')}]const doSomething = (idleDeadline) => {// idleDeadline.didTimeout // 是否过期 过期了就强制执行该任务 // idleDeadline.timeRemaining() // 返回值是剩余时间 while ((idleDeadline. timeRemaining() > 0 || idleDeadline.didTimeout) && worksQueue.length) {console . log(idleDeadline.didTimeout)console . log(idleDeadline. timeRemaining())const work = worksQueue. shift()work()}// 还有任务 if (worksQueue.length) requestIdleCallback(doSomething, { timeout: 10 })}requestIdleCallback(doSomething, { timeout: 100 })
下一篇:女生发了6799是啥意思