0

我正在从事一个严重依赖计时器的项目。我已经看到了 1-2 个场景,其中计时器变慢并且没有按应有的每秒滴答声,但是 1 秒/滴答声需要 2-3 秒。通常是由于整个计算机速度变慢并且变得非常滞后。我无法复制它,但一些用户报告了类似的发现,一旦他们重置了他们的计算机,一切都应该是这样。

我的问题是,超时/间隔如何工作?如果浏览器卡住了,它会减慢计时器的速度,还是基于 CPU/系统时间?另外,有没有其他人发生过类似的事情,如果是这样,我可以做些什么来纠正这种情况(即使系统很慢,也可以让一个 15 秒的计时器在 15 秒后实际触发一个事件)?

4

2 回答 2

4

默认情况下,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:旋转木马并不酷——我知道很多人会很好地学习这个!)

总之,我有点啰嗦了。我不知道我是否回答了你的问题,但我当然希望我已经启发了你一些,并且你发现这很有趣,如果是切线的话。

玩得开心!

于 2013-10-23T17:58:54.637 回答
1

这是setTimeout上的有用链接。(由约翰·雷西格撰写)

文章总结:

  • JavaScript 引擎只有一个线程,强制异步事件排队等待执行。
  • setTimeout 和 setInterval 在执行异步代码的方式上有着根本的不同。
  • 如果计时器被阻止立即执行,它将被延迟到下一个可能的执行点(这将比所需的延迟更长)。
  • 如果间隔需要足够长的时间来执行(比指定的延迟时间长),则间隔可以无延迟地连续执行。
于 2013-10-23T17:44:46.183 回答