Creating a clock with setInterval

If you have ever created a Javascript clock, you may have noticed some issues with using setInterval to update the time.

The clock ticks at irregular intervals and can get farther and farther from the correct time.

Below is a possible first attempt:

<div id="clock"></div>
var date = Date.now(),
  second = 1000;

function pad(num) {
  return ('0' + num).slice(-2);
}

function updateClock() {
  var clockEl = document.getElementById('clock'),
    dateObj;
  date += second;
  dateObj = new Date(date);
  clockEl.innerHTML = pad(dateObj.getHours()) + ':' + pad(dateObj.getMinutes()) + ':' + pad(dateObj.getSeconds());
}

setInterval(updateClock, second);

If you check back on this clock after a few minutes, you will notice that the time gets off. Let’s try something else. How about we check the time/date on each tick and render the correct time.

var second = 1000;

function pad(num) {
  return ('0' + num).slice(-2);
}

function updateClock() {
  var clockEl = document.getElementById('clock'),
    dateObj = new Date();
  clockEl.innerHTML = pad(dateObj.getHours()) + ':' + pad(dateObj.getMinutes()) + ':' + pad(dateObj.getSeconds());
}

setInterval(updateClock, second);

That works a lot better, the clock is no longer getting off the original time. But, if you watch the clock closely, you will see it occasionally skip a second and it is often not changing at the exact same time as the system clock.

That happens because setInterval does not always call the function every second. It executes at (1 second) + (time Chrome is doing other things). Read my previous blog post on setInterval to get more details Understanding setInterval . It also starts at a random time. It may be at the beginning or the end of the second.

Here is an output of the rendering times.

As you can see the last 3 numbers are increasing each tick. That is not ideal because it means the clock is becoming more and more incorrect. The next step is to take into account the rendering time and try to render on the exact second. This will happen when Date.now() has 000 at the end (because it is in milliseconds). We can use the % operator to calculate the next time. Let’s forget setInterval because we need more fine grained control over the next tick time.

var second = 1000;

function pad(num) {
  return ('0' + num).slice(-2);
}

function updateClock() {
  var clockEl = document.getElementById('clock'),
    dateObj = new Date();
  clockEl.innerHTML = pad(dateObj.getHours()) + ':' + pad(dateObj.getMinutes()) + ':' + pad(dateObj.getSeconds());
}

function clockInterval(fn) {
    var time = second - (Date.now() % second);

    setTimeout(function() {
        fn();
        clockInterval(fn);
    }, time);
}

clockTimer(updateClock);

Now we have a clock that shows the right time and ticks at the correct time. It still isn’t perfect, but it’s as good as we can do while not blocking the event loop.

If you want to use this code, I created a github repo and a npm module. If you have ideas to make this better, please post in the comments.