Javascript setInterval doesn't work correctly when tab is not active

3.4k views Asked by At
function initTimer(timeLeft) {
     var Me = this,
     TotalSeconds = 35,
     Seconds = Math.floor(timeLeft);

     var x = window.setInterval(function() {
         var timer = Seconds;

         if(timer === -1) { clearInterval(x); return; }

            $('#div').html('00:' + (timer < 10 ? '0' + timer : timer));
            Seconds--;

         },1000);
     }

I have this code. Everything works fine, when this tab is active in browser, but when I change tab and return in tab later it has problems. To be more precise, it Incorrectly displays the time.

I'd also tried setTimeout, but problem was the same.

One idea, which I have is: HTML5 Web Workers...

But here is another problem... browsers support.

can someone help to solve this problem? How can I write setInterval, which works properly,even when tab is not active

2

There are 2 answers

0
Matt Burland On BEST ANSWER

Use the Date object to calculate time. Don't rely on a timer firing when you ask it to (they are NOT real-time) because your only guarantee is that it'll not fire before you ask it to. It could fire much later, especially for an inactive tab. Try something like this:

function  initTimer(periodInSeconds) {
            var end = Date.now() + periodInSeconds * 1000;


            var x = window.setInterval(function() {
                var timeLeft = Math.floor((end - Date.now()) / 1000);

                if(timeLeft < 0) { clearInterval(x); return; }

                $('#div').html('00:' + (timeLeft < 10 ? '0' + timeLeft : timeLeft));
            },200);
        }

initTimer(10);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="div"></div>

Note that by checking it more frequently we can make sure it's never off by too much.

0
The Spooniest On

JavaScript timers are not reliable, even when the tab is active. They only guarantee that at least as much time as you specified has passed; there is no guarantee that exactly that amount of time, or even anything close to it, has passed.

To solve this, whenever the interval fires, note what time it is. You really only need to keep track of two times: the current time, and the time that the previous interval fired. By subtracting the previous tick's time from the current tick's time, you can know how much time has actually passed between the two, and run your calculations accordingly.

Here's a basic outline of how something like this might look:

function  initTimer(timeLeft) {
        var Me           = this,
            TotalSeconds = 35,
            Seconds      = Math.floor(timeLeft),
            CurrentTime  = Date.now(),
            PreviousTime = null;

        var x = window.setInterval(function() {
            var timer = Seconds,
                timePassed;
            PreviousTime = CurrentTime;
            CurrentTime = Date.now();
            timePassed = CurrentTime - PreviousTime;

            if(timer < 0) { clearInterval(x); return; }

            $('#div').html('00:' + (timer < 10 ? '0' + timer : timer));
            Seconds = Seconds - timePassed;

        },1000);
    }