Kos Cos
Kos Cos

Reputation: 129

Countdown timer is lagging behind

I am trying to create countdown timer between 2 dates but the time is lagging behind after a while.

My PHP backend returns the difference between current time and X time in the future, for example current time and 2 hours in advance. This difference is passed to my HTML frontent in a .countdown class in the following format 03:20:15 which I use a javascript function to countdown the difference. Here is my function:

$(".countdown").each(function() {
   var $e = $(this);
   var interval = setInterval(function() { 
      var timer2 = $e.html();
      var timer = timer2.split(':');
      var hours = parseInt(timer[0], 10);
      var minutes = parseInt(timer[1], 10);
      var seconds = parseInt(timer[2], 10);
      --seconds;
      minutes = (seconds < 0) ? --minutes : minutes;
      hours = (minutes < 0) ? --hours : hours;
      if(hours < 0) {
         clearInterval(interval);
         window.location.reload();
      } else {
         seconds = (seconds < 0) ? 59 : seconds;
         seconds = (seconds < 10) ? '0' + seconds : seconds;
         minutes = (minutes < 0) ? 59 : minutes;
         minutes = (minutes < 10) ? '0' + minutes : minutes;
         hours = (hours < 10) ? '0' + hours : hours;
         $e.html(hours + ':' + minutes + ':' + seconds);
      }
   }, 1000);
});

The code works as expected but after a few minutes, lets say 2-3 minutes, if you refresh the page or open it in a new window you will see that the countdown timer was lagging behind by seconds/minutes. Does someone know what Im doing wrong?

Upvotes: 1

Views: 800

Answers (1)

Mx.Wolf
Mx.Wolf

Reputation: 678

You should compute the difference between (new Date()) and the target date. Use that difference and format new HTML string instead of parsing it to a hour, minutes, seconds value for decrementing.

details

The setInterval api specs suggest that delays due to CPU load, other tasks, etc, are to be expected. https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers

Your handler is called at approximately equal intervals while you consider them to be exact. At first iteration the actual time may differ from a planed time by some small amount (let say 4ms). Yet you are changing your counter by 1000 ms. As many more iterations passed this difference accumulates and become noticeable. A few minutes is enough to make this happens.

If you, on the other hand, pre-compute the target date-time value and will use the difference between current time and the target time your code will not be sensible to api inexactness.

$(".countdown").each(function () {
    var $e = $(this);

    const totalSeconds = (dt) => Math.floor(dt.valueOf() / 1000);

    const f1 = (timer2) => {
        var timer = timer2.split(':');
        var tdt = new Date().setHours(
            parseInt(timer[0]) + tdt.getHours(),
            parseInt(timer[1]) + tdt.getMinutes(),
            parseInt(timer[2]) + tdt.getSeconds());
        return totalSeconds(tdt);
    };

    const targetTime = f1($e.html());

    setInterval(function () {
        var timeSpan = targetTime - totalSeconds(new Date());

        if (timeSpan < 0) {
            window.location.reload();
        } else {
            var seconds = timeSpan % 60;
            var totalMinutes = Math.floor(timeSpan / 60);
            var minutes = totalMinutes % 60;
            var hours = Math.floor(totalMinutes / 60);

            $e.html(hours + ':' + minutes + ':' + seconds);;
        }
    }, 1000);
});

see also: https://jsfiddle.net/8dygbo9a/1/

Upvotes: 2

Related Questions