setInterval的定时器逐渐偏离准确的时间。

10 浏览
0 Comments

setInterval的定时器逐渐偏离准确的时间。

当我将setInterval设置为1000毫秒时,实际上它每隔大约1001毫秒触发一次函数。这导致了一个随着时间运行越久的缓慢时间漂移。

var start;
var f = function() {
    if (!start) start = new Date().getTime();
    var diff = new Date().getTime() - start;
    var drift = diff % 1000;
    $('
  • ').text(drift + "ms").appendTo('#results'); }; setInterval(f, 1000);
  • 当运行这段代码时,立即显示了不准确性。

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

    请自行查看: http://jsfiddle.net/zryNf/

    那么有没有更准确的方法来计时?或者有没有办法让setInterval更准确地工作?

    0
    0 Comments

    随着一点点的谷歌搜索,你会发现setInterval和setTimeout都不会在你指定的确切时间执行代码。使用setInterval(f,1000),它将至少等待1000毫秒后才执行,而不是准确等待1000毫秒。其他进程也在等待使用CPU的机会,这会导致延迟。如果你需要一个准确的计时器,可以使用更短的间隔,比如50毫秒,并与开始时间进行比较。不过我不会选择低于50毫秒,因为浏览器有一个最小间隔。

    以下是一些参考资料:

    “为了理解计时器是如何在内部工作的,需要探索一个重要的概念:计时器的延迟不能保证。由于浏览器中的所有JavaScript都在一个单线程上执行,异步事件(如鼠标点击和计时器)只有在执行中间有空闲时才会运行。这可以通过下面的示意图来进行最佳演示:”摘自:http://css.dzone.com/news/how-javascript-timers-work

    “Chrome和Chromium提供的平均间隔略大于41毫秒,在不到一分钟的时间内,第二个时钟就会明显慢下来。Safari的间隔略小于41ms,在性能上优于Chrome,但仍然不够好。我在Windows XP下进行了这些测试,但是在Windows 7下,Chrome的间隔平均在46ms左右。”摘自:http://www.goat1000.com/2011/03/23/how-accurate-is-window.setinterval.html

    50毫秒相当于20帧每秒;Chrome的回调函数上限为200帧每秒,即5毫秒;Firefox和IE的运行速度至少与250帧每秒一样快(我不确定它们是否有任何限制)。

    有趣的是,超高的tick速率似乎是一种蛮力解决方案。

    除了使用setInterval,可以尝试使用setTimeout,但下一个超时时间将是1000 + drift

    在我对这个主题的研究中,setInterval确实试图通过提前触发来弥补丢失的时间(至少在firefox22中是这样),但最终仍然会漂移。

    0
    0 Comments

    这是另一个自动修正的间隔函数。间隔被设置为一个较短的时间段,然后等待至少延迟一秒后触发。它不会总是准确地延迟1000ms后触发(看起来在0-6ms之间),但它会自动修正,不会出现漂移。

    更新以使用回调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);
    

    问题原因:

    setInterval函数的执行时间并不是精确的,它的时间间隔可能会因为JavaScript引擎以及其他因素的影响而发生漂移。

    解决方法:

    为了解决这个问题,作者使用了setTimeout函数代替setInterval,并在每次执行之后根据实际的时间间隔进行修正。通过计算实际执行时间与预期执行时间之间的差异,作者可以确定是否需要进行修正,并在下一次执行之前等待适当的时间来纠正漂移。

    以上是作者使用JavaScript提供的方法来解决setInterval时间漂移的问题的示例代码。通过使用setTimeout并在每次执行之后进行修正,可以确保间隔函数的定时准确性。

    0
    0 Comments

    setInterval timing slowly drifts away from staying accurate的问题是由于setInterval函数的计时器不够准确导致的。setInterval函数在每次调用之后会等待一定的时间间隔再执行下一次调用,但是由于浏览器的性能和其他因素的影响,setInterval函数的定时器会逐渐偏离预期的准确时间。

    解决这个问题的方法是通过补偿来调整定时器的调用时间。在给定的代码示例中,作者通过测量每次调用的时间差(drift)来补偿下一次调用的时间间隔。如果当前调用比预期时间晚1ms、2ms甚至10ms,那么下一次调用的时间间隔会相应地缩短,以便补偿这个时间差。这样只要每次调用的不准确性是固定的,时钟就不会丢失时间,这种方法就可以很好地工作。

    为了方便使用,作者还封装了一个名为accurateInterval的全局函数,该函数几乎可以替代setInterval。该函数的代码可以在给定的链接中找到。需要注意的是,由于夏令时的影响,函数在某些情况下可能会有一帧时间的延迟或者在一小时内以负的时间间隔连续触发。

    还有使用performance.now()函数替代new Date().getTime()来获取更准确的时间。performance.now()函数可以提供更高的精度,并且可以避免夏令时和其他与地区相关的问题。

    总之,解决setInterval定时器不准确的问题的方法是通过补偿调整下一次调用的时间间隔,并可以考虑使用performance.now()函数来获取更准确的时间。

    0