Will setInterval drift?

Solution 1:

Short answer: No, you can't be sure. Yes, it can drift.

Long answer: John Resig on the Accuracy of JavaScript Time and How JavaScript Timers Work.

From the second article:

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.

Both articles (and anything on that site) is great reading, so have at it.

Solution 2:

Here's a benchmark you can run in Firefox:

var start = +new Date();
var count = 0;
setInterval(function () {
    console.log((new Date() - start) % 1000,
    ++count,
    Math.round((new Date() - start)/1000))
}, 1000);

First value should be as close to 0 or 1000 as possible (any other value shows how "off the spot" the timing of the trigger was.) Second value is number of times the code has been triggered, and third value is how many times the could should have been triggered. You'll note that if you hog down your CPU it can get quite off the spot, but it seems to correct itself. Try to run it for a longer period of time and see how it handles.

Solution 3:

(Sorry about my bad english) I had same problem about counting down function, i writed a function countdown() and loop with setInterval but its drifting 1-3 milliseconds per loop. Then i write a function that controls is there any drifting and fixed it.

It controls with real minute and second only. Here it is. Its works fine to me, i hope it will help you too.

$.syncInterval(functionname,interval,controlinterval)

example:

countdown(){ some code };
$.syncInterval(countdown,1000,60);

it says run countdown function every 1000 milliseconds and check it every 60 seconds

here is the code:

$.syncInterval = function (func,interval,control) { 
        var 
        now=new Date();
        realMinute=now.getMinutes(),
        realSecond=now.getSeconds(),
        nowSecond=realSecond,
        nowMinute=realMinute,
        minuteError=0,
        countingVar=1,
        totalDiff=0;

        var loopthat = setInterval(function(){

        if (nowSecond==0) {
            nowMinute++;
            nowMinute=nowMinute%60;
        };
        if (countingVar==0){

            now=new Date();
            realSecond=now.getSeconds();
            realMinute=now.getMinutes();

            totalDiff=((realMinute*60)+(realSecond))-((nowMinute*60)+(nowSecond));
            if(totalDiff>0){
                for (i=1;i<=totalDiff;i++) {
                    func();
                    nowSecond++;
                    countingVar++;
                };
            } else if (totalDiff==0){
                func();
                nowSecond++;
                countingVar++;
            } else if (totalDiff<0) {

            };
        } else {
            func();
            nowSecond++;
            countingVar++;
        };
        countingVar=countingVar%control;
        nowSecond=nowSecond%60;
    },interval);
};