Benjamin Crouzier
Benjamin Crouzier

Reputation: 41945

Run a function at the exact beginning of each second (or at a specific number of milliseconds each second)

I would like to run a function at the beginning of every second.

With the following:

function loop() {
    console.log('loop', new Date());
}

setInterval(loop, 1000);

I get this: (running with node v11.13.0)

> node loop.js
loop 2019-04-04T17:37:24.198Z
loop 2019-04-04T17:37:25.222Z
loop 2019-04-04T17:37:26.228Z
loop 2019-04-04T17:37:27.229Z
loop 2019-04-04T17:37:28.230Z
loop 2019-04-04T17:37:29.231Z
loop 2019-04-04T17:37:30.235Z
loop 2019-04-04T17:37:31.239Z

You can see that at each loop, the number of milliseconds is offset by about 200ms, and it is increasing by a few milliseconds each iteration.

Ideally I would want this:

> node loop.js
loop 2019-04-04T17:37:24.000Z
loop 2019-04-04T17:37:25.000Z
loop 2019-04-04T17:37:26.000Z
...

Upvotes: 0

Views: 183

Answers (1)

Benjamin Crouzier
Benjamin Crouzier

Reputation: 41945

One way is to dynamically adjust the timeout before the next invocation, like so:

function loop() {
    let now = new Date();
    let millis = now.getMilliseconds();
    console.log('loop', now, now.getMilliseconds());

    setTimeout(loop, 1000 - millis)
}

loop();

This outputs:

node loop.js
loop 2019-04-04T17:42:55.311Z 311
loop 2019-04-04T17:42:56.022Z 22
loop 2019-04-04T17:42:57.005Z 5
loop 2019-04-04T17:42:58.000Z 0
loop 2019-04-04T17:42:59.001Z 1
loop 2019-04-04T17:43:00.001Z 1

Try for yourself:

function loop() {
  let now = new Date();
  let millis = now.getMilliseconds();
  console.log('loop', now, now.getMilliseconds());

  setTimeout(loop, 1000 - millis)
}

loop();

The first invocation is not at the beginning of the second (it's random), and the offset can vary by a few milliseconds in the following iterations. But that might be good enough depending on your use case.

Upvotes: 3

Related Questions