# requestAnimationFrame

# requestAnimationFrame 是什么

requestAnimationFrame 是 H5 新增的一个 API,目的是用于解决定时器不准的问题。requestAnimationFrame 接收一个回调函数,并会在浏览器下一帧渲染之前调用这个回调函数。

# setInterval 准确吗

答案是不准确的。推荐阅读官方文档:原因如下:setInterval() - Web API | MDN (mozilla.org) (opens new window)

  1. 当 setInterval 嵌套层级超过 5 层时,小于 4 毫秒的定时器设定将会被强制固定为 4 毫秒
  2. 由于 JS 的单线程设定,setInterval 根本不在乎主线程是否空闲,只要设定的时间到达,他就会将回调加入到任务队列中,至于 JS 主线程是否空闲、是否能及时执行该任务,无法保证。所以,回调的执行时间不一定是准确的。
  3. 当 setInterval 的回调执行时间大于我们设置的时间间隔时,真实的时间间隔一定是大于我们设置的时间的。因为除了第一次外,后面每一次向任务队列添加回调函数时,JS 主线程一定不是空闲的,至少还在执行上一次 setInterval 添加的回调任务。因此,如果我们回调任务的时间可能大于我们设定的时间间隔,推荐使用 settimeout 循环的方式。 采用这种方式,虽然也不能保证时间间隔准确,但是可以保证在下一个回调开始时,上一个回调一定已经完成

# setTimeout 准确吗

虽然在某些情况下,我们推荐使用循环 settimeout 的方式来模拟实现 setInterval,但这并不代表 settimeout 就是准确的。实际上,settimeout 同样不准确,原因和 setInterval 类似。setTimeout() 全局函数 - Web API | MDN (mozilla.org) (opens new window)

  1. 当 settimeout嵌套层级超过 5 层时,小于 4 毫秒的定时器设定将会被强制固定为 4 毫秒

  2. 为了减少损耗,对于不活动的浏览器标签页(非当前展示的标签页),浏览器会对 settimeout 强制设置一个最小的超时值,甚至可能直接取消 settimeout。 这方面的具体情况与浏览器有关:

    • Firefox 桌面版和 Chrome 针对不活动标签都有一个 1 秒的最小超时值。
    • 安卓版 Firefox 浏览器对不活动的标签有一个至少 15 分钟的超时,并可能完全卸载它们
  3. 由于 JS 的单线程设定,settimeout 根本不在乎主线程是否空闲,只要设定的时间到达,他就会将回调加入到任务队列中,至于 JS 主线程是否空闲、是否能及时执行该任务,无法保证。所以,回调的执行时间不一定是准确的。

  4. 超时时间有最大值。浏览器内部以 32 位带符号整数存储延时。这就会导致如果一个延时大于 2147483647 毫秒(大约 24.8 天)时就会溢出,导致定时器将会被立即执行。这是少见的提前执行的例子

# requestAnimationFrame 准确吗

从时间上来看, requestAnimationFrame 未必准确,它的执行时间和时间并无直接关联,而是在浏览器重新渲染页面前执行,所以我们无法设定它在某个时间点执行(尽管大多数浏览器的刷新频率为 60 HZ,但浏览器的实时刷新频率是无法保证的)。但是它可以保证回调一定是在下一次浏览器重新渲染页面时执行,所以也可以说 requestAnimationFrame 是准确的。

# requestAnimationFrame 的简单用法

requestAnimationFrame 的用法和 settimeout 类似,但是 requestAnimationFrame 无法设置超时时间。

   const timer = requestAnimationFrame(()=>{
            console.log(123);// 123
        })

取消 requestAnimationFrame :

  cancelAnimationFrame(timer)

# 兼容性处理

由于 requestAnimationFrame 是 H5 的一个新 API,所以在兼容性方面,可能并不是所有浏览器都实现了这个 API。所以,我们在使用的时候可以对 requestAnimationFrame 的兼容性做一个简单的处理。

一般浏览器的刷新频率为 60HZ,所以,在每一帧的刷新间隔为 1000/60 ,约等于 17ms。所以,如果浏览器不支持 requestAnimationFrame API,那么我们可以使用 settimeout 来模拟 requestAnimationFrame,设置超时时间为 17ms即可。

if(!window.requestAnimationFrame){
    requestAnimationFrame = function(fn){
        setTimeout(fn,17)
    }
}