0

I've ran into a known issue with chrome not being precise for setInterval() calls. Consider the following code (jsfiddle: http://jsfiddle.net/periklis/j6AU8/), which displays a simple timer:

<span id = "time_left"></span>
<script>
    seconds = 0;
    minutes = 10;
    hours = 0;

    setInterval(function () {
        if (seconds >= 1) {seconds--;}
        else {
            if (seconds == 0 ) {seconds = 59;}
            if (minutes >= 1)  {minutes--;}
            else {
                if (minutes == 0) {minutes = 59;}
                if (hours >= 1)   {hours--;}
                else              {hours = 0;}
            }
        }
        min = minutes.toString();
        sec = seconds.toString()
        if (min.length == 1) {min = "0" + min;}
        if (sec.length == 1) {sec = "0" + sec;}

        document.getElementById("time_left").innerHTML = hours + ":" + min + ":" + sec;

    }, 1000);
</script>

Using chromium (28.0.1500.52), I've opened the same script in 2 tabs, one having the focus and one hidden. After a while, if I switch to the hidden one, the timer has lagged behind for several seconds. The same holds true if the period is larger than 1'', say 2 or 3.

According to the link provided above, chrome should only exhibit similar behavior for calls less than 1''. Is there any (pure js, not jquery), work around this issue?

Community
  • 1
  • 1
periklis
  • 10,102
  • 6
  • 60
  • 68

1 Answers1

2

This timer doesn't lag, because it checks the 'real' time using the Date object.

var endTime = new Date;
endTime.setMinutes(then.getMinutes()+10);

refresh();

function refresh(){
    var diff = Math.abs((endTime-new Date().getTime())/1000);
    document.getElementById("time_left").innerHTML = 
               zeroPad(Math.floor(diff/3600)) +
         ":" + zeroPad(Math.floor(diff/60))+
         ":" + zeroPad(Math.floor((diff%60)));

    if (new Date < endTime){
        setTimeout(refresh,1000);
    }
}

// zeroPad method:
function zeroPad(nr,base,chr){
        var  len = (String(base||10).length - String(nr).length)+1;
        return len > 0? new Array(len).join(chr||'0')+nr : nr;
}

JsFiddle

BTW: using setTimeout has the advantage of being able to stop the timer any time (see also...).

Here's a page to test the accuracy of timeouts, used in this SO Question.

Community
  • 1
  • 1
KooiInc
  • 119,216
  • 31
  • 141
  • 177