默认情况下,setTimeout
使用系统的内部计时器,其粒度为 16.66...ms(频率为 60 Hz,即每秒 60 帧)。一些浏览器会覆盖这一点(这实际上对处理器来说真的很糟糕——尽管在 Internet Explorer 的情况下,它只有在计算机专门设置为“高性能”模式时才会这样做,否则它使用危害较小的 60fps 限制)
然而,即使setTimeout
完全准确,处理其中的 JavaScript 也需要时间。正如我们所希望的那样,代码并不是真正实时的。这意味着,即使使用完全准确的 1,000 毫秒计时器,经过几千次迭代后,您也会开始注意到明显的差异。
总而言之,setTimeout
(并且通过扩展setInterval
,基本上只是setTimeout
自动更新)是不可靠的。如果你想要可靠性,你必须使用增量时序。
增量计时是您保存启动计时器的时间,并将其与计时器触发的时间进行比较。一个简单的例子是这样的:
var start = new Date().getTime();
setInterval(function() {
var now = new Date().getTime(), delta = now-start;
console.log("Timer fired after "+delta+"ms");
// chances are, you'll see numbers between 984 and 1016, unless your browser
// is a processor-killing, rainforest-destroying monster :p
// even then, you'll probably see some 999s and 1001s, depending on how
// busy your computer is with other tasks. Open a YouTube video, you'll see.
},1000);
requestAnimationFrame
有了新功能,这变得更加容易。但是,请记住,这是一项新功能,大多数浏览器都没有在没有供应商前缀的情况下采用它。Internet Explorer 是我所知道的唯一浏览器。人们说 IE 很烂!
无论如何,假设你有一个合适的 polyfill,它看起来像这样:
var previous = new Date().getTime(); // or Date.now() if you're feeling brave...
// or are already excluding old browsers via canvas use
requestAnimationFrame(function(now) {
var loopit = arguments.callee, delta = now-previous;
previous = now;
console.log(delta); // should be around 16 or 17
requestAnimationFrame(loopit);
});
对于基本上只是荣耀的东西,似乎需要做很多工作setTimeout
,对吧?
嗯,是。但我特别喜欢的一件事是,它requestAnimationFrame
会等到一帧真正被渲染。特别是,这意味着如果您正在查看不同的选项卡,requestAnimationFrame
将等待您回来。我用它来延迟自动更新 AJAX 请求,允许用户在现场实际看到更新,而不是回来对自己说“现在......它更新了吗?我不知道,我不知道记住它之前的样子……”相反,他们会欣赏一些简洁的过渡,然后来告诉你在更新之前等待你回来是多么的酷;)当你的用户认为某些东西很酷时,那就是真的很酷。(Protip:旋转木马并不酷——我知道很多人会很好地学习这个!)
总之,我有点啰嗦了。我不知道我是否回答了你的问题,但我当然希望我已经启发了你一些,并且你发现这很有趣,如果是切线的话。
玩得开心!