将requestAnimationFrame用于非动画目的有什么好处?

问题描述

我制作了一个“睡眠”计时器函数,该函数递归调用requestAnimationFrame并在时间用完后返回一个Promise。使用requestAnimationFrame可以为我带来任何性能优势,还是我应该只使用setTimeout?我知道requestAnimationFrame在非常短/快速的等待时间(又称为动画帧)中具有更好的性能,但是对于接近或超过一秒的等待时间,有什么区别吗?

var sleep = (time) =>{
  var timer = (t,s,d,r)=>{
    if(t - s > d){
      r("done")
    } else {
      requestAnimationFrame((newT)=>{ timer(newT,r)})
    }
  }
  return new Promise((r)=>{
    requestAnimationFrame((t)=>{timer(t,t,time,r)})
  })
}

(async ()=>{
  var message = document.getElementById("message")
  while(message){
    message.innerText = "Get"
    await sleep(1000)
    message.innerText = message.innerText + " Ready"
    await sleep(1000)
    message.innerText = message.innerText + " To"
    await sleep(1000)
    message.innerText = message.innerText + " Wait"
    await sleep(1000)
  }
})()
<p style="text-align:center; font-size:20px;" id="message"></p>

解决方法

requestAniamtionFrame并非只是计时方法,顾名思义,它也 请求 动画帧。 / p>

在浏览器中,event loop processing model首先执行常规任务,然后when the browser thinks it should update the rendering调用此处理模型的一个特殊部分,该部分将很好地update the rendering

在大多数事件循环迭代中,进入处理模型的此可选部分所需的条件均未得到满足,因此不进行渲染,这很好,因为渲染成本很高。

即使规格留给实施者选择他们将用来决定何时更新渲染的试探法的选择,但大多数人仍将监视器的刷新率用作基本频率。
除了此监视器的刷新事件外,它们仅更新标记为需要更新的文档的呈现。
大多数时候,不需要重新渲染网页,只有在运行动画或用户与之交互时,它们才会将文档标记为需要重新渲染。

requestAnimationFrame是让我们的Web开发人员在此更新渲染子过程中挂接的工具,它使我们仅在渲染将要发生时才可以绘制内容,但它也具有标记Web的副作用页面设置为 animated (动画),因此强制浏览器执行完整的 update渲染步骤,即使可能不需要。

例如,使用Chrome的Performance开发工具,您可以在下面的代码段中看到,一个简单的空rAF循环会导致 Composite Layer 操作在每次屏幕刷新时都被触发,而这只会发生当鼠标以其他方式移动时,以及仅因为鼠标内部移动调用了rAF)。

const input = document.querySelector("input");
input.oninput = (evt) => {
  rAFLoop();
};
function rAFLoop() {
  if( input.checked ) {
    requestAnimationFrame( rAFLoop );
  }
}
<label><input type="checkbox" id="inp">activate rAF loop</label>

因此,尽管requestAnimationFrame是视觉动画的绝佳工具,但它也带有责任感,对于您而言,您是在滥用它。

使用简单的setTimeout是可行的方法,不仅与呈现过程没有关系,而且即使没有任何作用,它甚至可以使浏览器进入 idle 否则,让您的计算机执行其他任务或保存树。

const sleep = (ms) =>
  new Promise( (res) =>
    setTimeout( () => res(),ms )
  );
(async ()=>{
  var message = document.getElementById("message")
  while(message){
    message.innerText = "Get"
    await sleep(1000)
    message.innerText = message.innerText + " Ready"
    await sleep(1000)
    message.innerText = message.innerText + " To"
    await sleep(1000)
    message.innerText = message.innerText + " Wait"
    await sleep(1000)
  }
})()
<p style="text-align:center; font-size:20px;" id="message"></p>