1

So I'm creating a clock for my website, and I have thought of 2 approaches to it..

1)

var today = new Date();
this.start = function() {
    this.update();
    setInterval(this.update,1000);
}
this.update = function() {
    today.setSeconds(today.getSeconds() + 1);
}

or

2)

var today = new Date();
this.start = function() {
    this.update();
    setInterval(this.update,1000);
}
this.update = function() {
    today = new Date();
}

Which would be better? Generating a new date every second or just updating? (or perhaps a third approach)

sigmaxf
  • 7,998
  • 15
  • 65
  • 125

1 Answers1

3

It's much better for you to use new Date() for each loop because the interval used on setInterval will not be 100% constant.

new Date() will be accurate regardless of setInterval variances


Here's a snippet from an answer I gave a couple days ago. It explains the issue with using setInterval with fixed-value incrementing (or decrementing).

Beware of creating timers that increment with a fixed value

In your code, you have

setTimeout(() => this.count--, 1000);

The intention is for you to decrement your count property once every second, but this is not the behavior you will be guaranteed.

Check out this little script

var state = {now: Date.now()};

function delta(now) {
  let delta = now - state.now;
  state.now = now;
  return delta;
}

setInterval(() => console.log(delta(Date.now())), 1000);

// Output
1002
1000
1004
1002
1002
1001
1002
1000

We used setInterval(fn, 1000) but the actual interval varies a couple milliseconds each time.

The problem is exaggerated if you do things like switch your browser's focus to a different tab, open a new tab, etc. Look at these more sporadic numbers

1005 // close to 1000 ms
1005 // ...
1004 // a little variance here
1004 // ...
1834 // switched focus to previous browser tab
1231 // let timer tab run in background for a couple seconds
1082 // ...
1330 // ...
1240 // ...
2014 // switched back to timer tab
1044 // switched to previous tab
2461 // rapidly switched to many tabs below
1998 // ...
2000 // look at these numbers...
1992 // not even close to the 1000 ms that we set for the interval
2021 // ...
1989 // switched back to this tab
1040 // ...
1003 // numbers appear to stabilize while this tab is in focus
1004 // ...
1005 // ...

So, this means you can't rely upon your setTimeout (or setInterval) function getting run once per 1000 ms. count will be decremented with much variance depending on a wide variety of factors.

To work around this, you need to use a delta. That means before each "tick" of your timer, you need to take a timestamp using Date.now. On the next tick, take a new timestamp and subtract your previous timestamp from the new one. That is your delta. Using this value, add it to the Timer's total ms to get the precise number of milliseconds the timer has been running for.


Community
  • 1
  • 1
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Nice, thanks! The idea is to just update a simple clock so I won't need to find out the delta.. but your answer is very complete. I noticed that when using settimeout the clock would get out of sync after some time – sigmaxf Jul 24 '15 at 14:45
  • Yeah, in your case, you won't need to compute the delta, just make sure you're not using the `today.setSeconds(today.getSeconds() + 1);` variety. That is a fixed-value increment and will get out of sync very quickly. – Mulan Jul 24 '15 at 14:46
  • 1
    Also look at [moment.js](http://momentjs.com/) for *all* of your JavaScript date/time needs. – Mulan Jul 24 '15 at 14:47