setInterval timing slowly drifts away from staying accurate

I think I may have figured out a solution. I figured, if you can measure it you can compensate for it, right?

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();

result varies a bit but here's a recent run:

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

So if it gets called 1ms, 2ms or even 10ms later than it should the next call is scheduled to compensate for that. As long as inaccuracy is only per call, but the clock should never lose time, then this should work well.


And now I wrapped this up a global accurateInterval function which is a near drop in replacement for setInterval. https://gist.github.com/1d99b3cd81d610ac7351


Here's another autocorrecting interval. The interval is set to a shorter time period and then it waits until it's at least a second later to fire. It won't always fire exactly 1000ms later (seems to range from 0-6ms delay), but it autocorrects and won't drift.

EDIT: Updated to use recalling setTimeout instead of setInterval otherwise it may do something odd after 1000 or so iterations.

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


with a bit of googleing, you will see thatsetInterval and settimeout both will not execute the code at the exact specified time you tell it. with setInterval(f,1000); it will wait AT LEAST 1000MS before it executes, it will NOT wait exactly 1000MS. Other processes are also waiting for their turn to use the CPU, which causes delays. If you need an accurate timer that times at 1 second. I would use a shorter interval, like 50MS and compare it to the start time. I wouldnt go under 50MS though because browsers have a minimum interval

here are a few references:

"In order to understand how the timers work internally there's one important concept that needs to be explored: timer delay is not guaranteed. Since all JavaScript in a browser executes on a single thread asynchronous events (such as mouse clicks and timers) are only run when there's been an opening in the execution. This is best demonstrated with a diagram, like in the following:" taken from: http://css.dzone.com/news/how-javascript-timers-work

"Chrome and Chromium provide an interval that averages just over 41 milliseconds, enough of a difference for the second clock to be visibly slower in well under a minute. Safari comes in at just under 41ms, performing better than Chrome, but still not great. I took these readings under Windows XP, but Chrome actually performed worse under Windows 7 where the interval averaged around 46ms." taken from: http://www.goat1000.com/2011/03/23/how-accurate-is-window.setinterval.html

Tags:

Javascript