# requestAnimationFrame
# requestAnimationFrame 是什么
requestAnimationFrame 是 H5 新增的一个 API,目的是用于解决定时器不准的问题。requestAnimationFrame 接收一个回调函数,并会在浏览器下一帧渲染之前调用这个回调函数。
# setInterval 准确吗
答案是不准确的。推荐阅读官方文档:原因如下:setInterval() - Web API | MDN (mozilla.org) (opens new window)
- 当 setInterval 嵌套层级超过 5 层时,小于 4 毫秒的定时器设定将会被强制固定为 4 毫秒

- 由于 JS 的单线程设定,setInterval 根本不在乎主线程是否空闲,只要设定的时间到达,他就会将回调加入到任务队列中,至于 JS 主线程是否空闲、是否能及时执行该任务,无法保证。所以,回调的执行时间不一定是准确的。
- 当 setInterval 的回调执行时间大于我们设置的时间间隔时,真实的时间间隔一定是大于我们设置的时间的。因为除了第一次外,后面每一次向任务队列添加回调函数时,JS 主线程一定不是空闲的,至少还在执行上一次 setInterval 添加的回调任务。因此,如果我们回调任务的时间可能大于我们设定的时间间隔,推荐使用 settimeout 循环的方式。
采用这种方式,虽然也不能保证时间间隔准确,但是可以保证在下一个回调开始时,上一个回调一定已经完成。
# setTimeout 准确吗
虽然在某些情况下,我们推荐使用循环 settimeout 的方式来模拟实现 setInterval,但这并不代表 settimeout 就是准确的。实际上,settimeout 同样不准确,原因和 setInterval 类似。setTimeout() 全局函数 - Web API | MDN (mozilla.org) (opens new window)
当 settimeout嵌套层级超过 5 层时,小于 4 毫秒的定时器设定将会被强制固定为 4 毫秒

为了减少损耗,对于不活动的浏览器标签页(非当前展示的标签页),浏览器会对 settimeout 强制设置一个最小的超时值,甚至可能直接取消 settimeout。 这方面的具体情况与浏览器有关:
- Firefox 桌面版和 Chrome 针对不活动标签都有一个 1 秒的最小超时值。
- 安卓版 Firefox 浏览器对不活动的标签有一个至少 15 分钟的超时,并可能完全卸载它们。
由于 JS 的单线程设定,settimeout 根本不在乎主线程是否空闲,只要设定的时间到达,他就会将回调加入到任务队列中,至于 JS 主线程是否空闲、是否能及时执行该任务,无法保证。所以,回调的执行时间不一定是准确的。
超时时间有最大值。浏览器内部以 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)
}
}
← node模块查找策略 WebView 入门 →