之前参加网易游戏笔试时,第一道大题就是问的requestAnimationFrame这个API,同时让说明这个API与setTimeout的异同。当时看到的时候完全不知道requestAnimationFrame是什么,直接放弃。想着回来还是得吧这个知识盲点给补上。
setTimeout
setTimeout由浏览器的定时器模块来管理,当达到所设置的定时时间,就会将定时器绑定的事件(函数)加入到宏任务队列中。不同异步任务的执行顺序可参考。
通过在setTimeout绑定的函数中再绑定一个setTimeout来模拟setInterval定时器,如下代码所示,这样的好处是避免动画的抖动,保证下一次动画函数执行必须在当前动画执行结束后的再执行。var i = 0;var timer;var foo = function() { console.log(i++); timer = setTimeout(foo, 1000)}foo();
但是,setTimeout并不能按照设置的定时准时触发,这点可以从js事件循环来解释。当设置了该定时器时,假设定时1000毫秒,
- 首先,绑定函数交由定时器模块处理;
- 然后,当到达1000毫秒时,定时器模块会将该绑定事件加入到任务队列中,注意,这个时候已经到了我们的预设时间了,但是实际上函数并没有被执行。
- 最后,当任务队列中优先于该任务的函数执行完成后,才会执行该定时器绑定的函数。所以这里的实际执行的时间相比预设时间会更晚。
requestAnimationFrame
requestAnimationFrame和屏幕刷新是同步的,也就是说屏幕每刷新一次,requestAnimationFrame就触发一次。
由于requestAnimationFrame与浏览器刷新同步,因此不会像setTimeout那样积累时间上的误差。加入屏幕刷新率是60,则最小间隔时间是16.67ms。var i = 0;var timer;var foo = function() { console.log(i++); timer = setTimeout(foo, 1000)}foo();
由于不同显示器的刷新率不同,因此如果要定时的话,时间也不相同。可以通过js来计算屏幕刷新率。
var refreshInterval;(function(){ var i = 0; var foo = function() { if (++i == 100) { refreshInterval = (new Date().getTime() - time1)/100; return; } } var time1 = new Date().getTime(); foo();}())