setInterval计时缓慢偏离准确

时间:2011-11-17 20:08:36

标签: javascript

似乎当我setInterval持续1000ms时,它实际上每1001ms左右触发一次该函数。这导致时间漂移缓慢,其运行时间越长。

var start;
var f = function() {
    if (!start) start = new Date().getTime();
    var diff = new Date().getTime() - start;
    var drift = diff % 1000;
    $('<li>').text(drift + "ms").appendTo('#results');
};

setInterval(f, 1000);

运行时会立即显示不准确信息。

  • 0毫秒
  • 1ms的
  • 2ms的
  • 3ms的
  • 4ms的
  • 5ms的
  • 5ms的
  • 7毫秒
  • 8ms的
  • 9ms的
  • 9ms的
  • 10ms的

亲眼看看:http://jsfiddle.net/zryNf/

那么有更准确的方法来保持时间吗?或者让setInterval表现得更准确的方法?

6 个答案:

答案 0 :(得分:14)

我想我可能已经找到了解决方案。我想,如果你可以测量它,你可以补偿它,对吗?

http://jsfiddle.net/zryNf/9/

var start;
var nextAt;

var f = function() {
    if (!start) {
        start = new Date().getTime();
        nextAt = start;
    }
    nextAt += 1000;

    var drift = (new Date().getTime() - start) % 1000;    
    $('<li>').text(drift + "ms").appendTo('#results');

    setTimeout(f, nextAt - new Date().getTime());
};

f();

结果略有不同但这是最近的一次运行:

0ms
7ms
2ms
1ms
1ms
1ms
2ms
1ms
1ms
1ms

因此,如果它被调用1ms,2ms甚至晚10ms,那么下一次呼叫将被安排来补偿。只要每次通话都不准确,但时钟永远不会浪费时间,那么这应该可以正常工作。


现在我把它包装成一个全局accurateInterval函数,这个函数几乎可以代替setIntervalhttps://gist.github.com/1d99b3cd81d610ac7351

答案 1 :(得分:8)

通过一些googleing,您会看到setIntervalsettimeout都不会在您指定的确切时间执行代码。使用setInterval(f,1000);它将在执行前等待至少1000MS,它不会等到1000MS。其他进程也在等待轮到他们使用CPU,这会导致延迟。如果你需要一个精确的计时器,时间为1秒。我会使用较短的间隔,如50MS,并将其与开始时间进行比较。我不会低于50MS,因为浏览器有最小间隔

以下是一些参考文献:

“为了理解定时器如何在内部工作,有一个需要探索的重要概念:定时器延迟无法保证。因为浏览器中的所有JavaScript都在单个线程上执行异步事件(例如鼠标点击和定时器)只有在执行中有一个开放时才会运行。最好用图表来证明,如下所示:“取自:http://css.dzone.com/news/how-javascript-timers-work

“Chrome和Chromium提供的平均时间间隔刚好超过41毫秒,第二个时钟的差异足以在一分钟内明显变慢.Safari的亮度不到41毫秒,表现优于Chrome,但仍然我在Windows XP下读取了这些读数,但Chrome在Windows 7下的表现实际上差了很多,平均时间间隔约为46毫秒。“取自: http://www.goat1000.com/2011/03/23/how-accurate-is-window.setinterval.html

答案 2 :(得分:4)

这是另一个自动校正间隔。间隔设置为较短的时间段,然后等待,直到它至少一秒钟后才能触发。它不会总是在1000毫秒之后发射(似乎在0到6毫秒的延迟范围内),但它会自动校正并且不会漂移。

修改 更新后使用召回setTimeout代替setInterval,否则在1000次左右的迭代后可能会发生奇怪的事情。

var start, tick = 0;
var f = function() {
    if (!start) start = new Date().getTime();
    var now = new Date().getTime();
    if (now < start + tick*1000) {
        setTimeout(f, 0);
    } else {
        tick++;
        var diff = now - start;
        var drift = diff % 1000;
        $('<li>').text(drift + "ms").appendTo('#results');
        setTimeout(f, 990);
    }
};

setTimeout(f, 990);

Run demo

答案 3 :(得分:1)

我看不到几乎和你的剧本一样大的漂移:
http://jsfiddle.net/hqmLg/1/

我要离开那个脚本了。现在(Chrome,Win 7)我明白了:

  

240.005s中的240个呼叫是0.99979个呼叫/秒

事实上,我已经看到漂移上升到.007s然后下降到.003s。我认为你的测量技术存在缺陷。

在Firefox中,我看到它漂移得更强(两个方向+/- 8ms),然后在下一次运行中进行补偿。大多数时候我在Firefox中看到“1.000000次/秒”。

答案 4 :(得分:0)

您可以使用此功能使通话保持在预期的时间表附近。它使用 Name ID_NB Rule_F 0 Alice 1715386 PDF FORMAT 3 Sarah 14881611 PDF FORMAT 4 Dee 846620760 PDF FORMAT 并根据经过的时间计算下一个通话时间。

setTimeout

答案 5 :(得分:0)

通过更改 Google Chrome 上的选项卡,可以轻松重现 setInterval 或 setTimeout 中的错误。 为了处理这些情况,您可能需要考虑为用户在另一个选项卡中的时间设置条件。

setTimeout(function() {
    if (!document.hasFocus()) {
        //... do something different, because more than 1 second might have passed
    }
}, 1000);