0

I have a javascript timer that is not working correctly. When I insert it into my browser the seconds are jumping back slightly. I believe it might be something to do with the milliseconds not being converted correctly in seconds/minutes/hours but I'm not sure. Here is my code:

  var link = userLink + '/games/' + $scope.currentGame;
      ref.child(link).once('value', function(snapshot) {
      var game = snapshot.val();
      var startTime = Date.parse(game.started);
      var timeNow = Date.now();
      var timeLapsed = (timeNow - startTime);
      var mydate = new Date(timeLapsed);
      var humandate = mydate.getUTCHours()+ " hours, " + mydate.getUTCMinutes()+ " minutes and " + mydate.getUTCSeconds()+ " second(s)";
      console.log(humandate);
      console.log( mydate.getUTCHours())
      var timeVar = document.getElementById('timer'), seconds = mydate.getUTCSeconds(), minutes = mydate.getUTCMinutes(), hours = mydate.getUTCHours(), time;

      function add() {
        seconds++;
        if (seconds >= 60) {
          seconds = 0;
          minutes++;
            if (minutes >= 60) {
              minutes = 0;
              hours++;
            }
      }
        timeVar.textContent = (hours ? (hours > 9 ? hours : "0" + hours) : "00") +
        ":" + (minutes ? (minutes > 9 ? minutes : "0" + minutes) : "00") +
        ":" + (seconds > 9 ? seconds : "0" + seconds);
        timer();
      }
      function timer() {
        var time = setTimeout(add, 1000);
      }
      timer();

    });

The initial starting time is grabbed from a firebase database for persistence (the timer is intended as part of game) and then the following functions increment the time. Any help would be appreciated. Thanks.

dellboyant
  • 27
  • 8
  • 1
    1. Please use the snippet editor to create an actual running clock. 2. JS Timers are notoriously dependent on what else is going on – mplungjan Nov 11 '15 at 13:37
  • Even though this is about `setTimeout`, this is still a duplicate of this: http://stackoverflow.com/questions/8173580/setinterval-timing-slowly-drifts-away-from-staying-accurate – Anders Marzi Tornblad Nov 11 '15 at 13:40
  • have a look here: https://github.com/developer82/javascript-stopwatch/blob/master/stopwatch.js see if that helps – developer82 Nov 11 '15 at 13:41
  • 1
    Compare two date objects to get the real elapsed time and make your timeout time smaller if you want the seconds to tick by realistically. – James Nov 11 '15 at 13:46

2 Answers2

0

This line:

var time = setTimeout(add, 1000);

doesn't execute every second, but every "the code execution time + 1 second" interval, so at least a few milliseconds over a second. The longer the code runs, the less accurate it will be. Try using setInterval once instead of calling setTimeout every second, as it will fire when you expect it.

Shomz
  • 37,421
  • 4
  • 57
  • 85
  • 2
    Even `setInterval` will not be enough. The most accurate way is to set a new timeout value at each call to `setInterval`, like this: `setTimeout(add, (1000 - (Date.now() % 1000)));` – Anders Marzi Tornblad Nov 11 '15 at 13:38
  • @atornblad They'd behave pretty much the same way. What happens when the delay goes over 1000 milliseconds? – Shomz Nov 11 '15 at 13:44
  • 1
    You will wait long. The cowards don't tell because that would be useful – mplungjan Nov 11 '15 at 13:59
  • 1
    @mplungjan Yeah, the good thing is that they are outnumbered by all the great people here on SO. – Shomz Nov 11 '15 at 13:59
  • @atornblad that has definitely helped however there are still problems. The timer is now moving up incrementally, but is now skipping a second every other 4th or 5th second i.e. 51, 52, 53, 54, 56 ...! Any suggestions? Thank you. – dellboyant Nov 11 '15 at 14:06
  • Run a shorter interval, and check the actual time in each call. The shorter the interval, the better the syncronization. – Shomz Nov 11 '15 at 14:10
0

You cannot rely on setTimeout for counting seconds (or time in general). setTimeout just postpone the function's execution for at least the amount of time (in milliseconds) you asked. So in your case it could be that the 'add' function runs after 1.5 seconds. The reason for that is because JavaScript runs in the same thread as the UI and it means that if there's anther task for the UI thread to do, it will hold your function execution. setInterval will not do the trick as well, for the same reason.

What you want to do is use the time object to know how much date really passed since the last time the function was invoked.

Udi Cohen
  • 1,259
  • 9
  • 12