2

I am trying to build an accurate countdown timer that shows mins and seconds left. I have tried 2 approaches. Approach 1 uses the setTimer function and calculates the drift. For that approach, some values get skipped and some values get repeated. Approach 2 yields all of the necessary values, but the values are not getting printed to the screen at even intervals (tested in repl.it). How can I make a timer that is both accurate and prints all of the values ?

Approach1:

function countTime(duration) {
     var expected = 1;
     var secsLeft;
     var startT = new Date().getTime();
     var oneSecond = 1000;
     var expected = startT + oneSecond;

     window.setTimeout(step, oneSecond);

     function step() {
         var nowT = new Date().getTime();
         var drift = nowT - expected;

         if (drift > oneSecond) {
             console.log("drift is over 1 second long!");
         }

         console.log('drift is ' + drift);
         var msDelta = nowT - startT;
         var secsLeft = duration - Math.floor(msDelta / 1000);
         console.log("secsLeft" + secsLeft);

         if (secsLeft === 0) {
             ++count;
             console.log("cleared");

         } else {

             expected += oneSecond;
             setTimeout(step, Math.max(0, oneSecond - drift));
         }
     }
 }
 countTime(60);

Approach2:

function countTime(duration) {
     var expected = 1;
     var secsLeft;
     var inter;
     var startT = new Date().getTime();

     inter = setInterval(function() {
         //change in seconds
         var sChange = Math.floor((new Date().getTime() - startT) / 1000);

         if (sChange === expected) {
             expected++;
             secsLeft = duration - sChange;
             console.log("seconds Left" + secsLeft);
         }

         if (secsLeft === 0) {
             window.clearInterval(inter);
             console.log("cleared");
         }
     }, 100);
 }
 countTime(60);
1
  • 1
    This is probably a candidate for requestAnimationFrame(). Commented Sep 3, 2015 at 22:10

2 Answers 2

1

Consider using requestAnimationFrame.

function countTime(duration) {
    requestAnimationFrame(function(starttime) {
        var last = null;
        function frame(delta) {
            var timeleft = Math.floor(duration - (delta - starttime)/1000),
                minutes = Math.floor(timeleft/60),
                seconds = timeleft%60;
            if( timeleft > 0) {
                if( last != timeleft) {
                    console.log("Time left: "+minutes+"m "+seconds+"s");
                    last = timeleft;
                }
                requestAnimationFrame(frame);
            }
        }
        frame(starttime);
    });
}
countTime(60);

This will be precise to within the framerate of the browser itself :)

Sign up to request clarification or add additional context in comments.

Comments

1
function(){
  date = get the date
  curSeconds = compute the number of seconds to be displayed
  if(oldSeconds!=curSeconds) then refresh display and do  oldSeconds = curSeconds;     
}

Call this function quite often, the more you call it, the more accurate your timer will be. I advise you to use requestAnimationFrame() instead of setTimout() it will be called 60 times per second (period 16ms) since it is the refresh rate of most displays, it is the maximum visible "accuracy". Also it won't be called when page is not visible.

Simple, clean, no drift over long periods of time.

It also handle not being called for a while.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.