2

I need to generate an event exactly every 1s. I'm testing setTimeout function as following:

   window.onload = function() {
        var oldTime = Date.now();

        setTimeout(function printTime() {
            newTime = Date.now();
            console.log(newTime - oldTime);
            oldTime = newTime;
            setTimeout(printTime, 1000);
        }, 1000);
    }

It returns 1s with a little bit offset:

testTimeout.js:6 1005
testTimeout.js:6 1006
2testTimeout.js:6 1001
testTimeout.js:6 1006
testTimeout.js:6 1005
testTimeout.js:6 1006
testTimeout.js:6 1005

Where does the offset come from? Is it because of the oldTime and newTime calculation?

Dzung Nguyen
  • 3,794
  • 9
  • 48
  • 86

1 Answers1

3

I need to generate an event exactly every 1s.

There is no way to do this with exact precision.

The time passed to setTimeout is the minimum guaranteed time in which your code will run, not the exact time.

The time represents in how many milliseconds the JS Engine will push that function to the Event Queue. Once the function is in the Event Queue, it runs after all the current tasks present in the queue, such as events and other timers, which will create a delay.

Additionally, no task can be executed from the Event Queue while any regular JS code is running which could also potentially create small delays.

For example, this function test:

setTimeout(function test() {
  console.log('second');
}, 0);
for (i = 0; i < 1000000; i++); // the number 1000000 is an arbitrary big number
console.log('first');

Will run after the loop below it has finished, which will be more than 0 milliseconds away.

In fact, if that loop was infinite, the setTimeout callback will never fire.


Note: As mentioned by nnnnnn in the comments, the actual minimum time, as per the HTML5 spec, in which setTimeout will push a function to the Event Queue is 4ms, regardless if we put less. Previously that limit used to be 10ms.

MDN

In fact, 4 ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.

nem035
  • 34,790
  • 6
  • 87
  • 99
  • 1
    In other words, you *can't* schedule a function to run at an *exact* time. (Also, regarding the 0ms timeout, even without a long-running loop after it it will still be a minimum of 4ms before it runs because browsers round smaller delays up to 4ms - though your point about other code having to finish first still stands.) – nnnnnn Aug 26 '16 at 00:47