requestAnimationFrame回调参数及与performance.now()的时间差原因探究
关于requestAnimationFrame回调参数与时间差的问题解答
一、requestAnimationFrame回调的参数是什么?
requestAnimationFrame的回调函数接收的参数t是一个DOMHighResTimeStamp类型的时间值,它代表浏览器准备开始重绘下一帧的精确时间点。这个时间的计算基准和performance.now()完全一致——都是从页面导航加载完成开始计时的毫秒数,精度可以达到微秒级别,适合做高精度的时间同步场景。
二、为什么t和performance.now()会有12ms的差异?
你代码里出现的时间差,本质是「浏览器预定的重绘时间」和「回调实际执行时的时间」之间的间隔,主要由以下几个因素导致:
- 浏览器重绘调度的时机差:requestAnimationFrame的回调只会在浏览器准备重绘的前一刻执行,而setInterval的调度完全独立于浏览器重绘周期。浏览器默认重绘帧率是60fps(约16.6ms每帧),你用20ms的间隔调用requestAnimationFrame,意味着很多时候你的回调注册请求会落在两次重绘之间,这时候回调必须等待下一次重绘时机才能执行——这中间的等待时间就会体现在
t和performance.now()的差值里。 - 主线程任务排队的延迟:当浏览器准备执行rAF回调时,如果主线程正在处理其他任务(比如之前未完成的JS代码、页面布局计算、其他已注册的rAF回调),当前回调的执行就会被阻塞。
t是浏览器预先确定的重绘时间,而performance.now()是回调真正开始执行的时间,这段阻塞的时间是差值的主要来源。 - setInterval本身的精度问题:setInterval的实际执行间隔并不严格等于你设置的20ms——如果主线程被阻塞,setInterval的任务会被延迟加入宏任务队列,导致你注册rAF回调的时间比预期晚,进一步拉大了
t和实际执行时间的差距。 - 回调内部的执行开销:虽然
console.log看起来很轻量,但它本身也需要消耗一定时间(比如字符串拼接、输出到控制台的操作),加上JS引擎解释执行代码的微小开销,也会贡献一部分差值,但通常这部分占比很小,12ms的差值主要还是前面两个因素导致的。
举个具体场景:假设浏览器在1000ms时准备重绘,执行完所有rAF回调后,你的setInterval在1020ms触发,注册了新的rAF回调。由于下一次重绘预定在1016.6ms(1000+16.6),但你的注册请求来得太晚,这个回调会被安排到下下一次重绘(1033.2ms),此时t就是1033.2。如果主线程还有其他任务等待执行,回调实际启动时间可能到1045ms左右,这时候performance.now()就是1045,和t的差就有12ms左右了。
总结
这个时间差是浏览器调度机制、主线程任务排队、定时器精度共同作用的结果。如果你的场景对这个时间差很敏感,建议不要用setInterval触发requestAnimationFrame,而是在rAF回调内部递归调用自己,这样能保证回调严格跟随浏览器的重绘周期,时间差会稳定在很小的范围内。
内容的提问来源于stack exchange,提问作者Vladimir.J.Nekiy
相关产品推荐
相关产品推荐

