Javascript is a language full of surprises: there are the good parts and the bad parts. Today, I discovered that setTimeout featured a minimal delay of 4 msec.

This is well documented in Reasons for delays longer than specified.

But I wonder, how many experienced javascript developers are aware of this weird behaviour.

What about you?

Did you know that the minimal delay of setTimeout was 4 msec?

In this article, we are going to:

  1. demonstrate the 4 sec delay added by the browser when one uses a setTimeout with 0 delay.
  2. demonstrate how to write 0-delay timers using postMessage.

Stop

Code example

All the code snippets of this page are live and interactive powered by the klipse plugin:

  1. Live: The code is executed in your browser
  2. Interactive: You can modify the code and it is evaluated after 3 seconds of inactivity of if you press Ctrl-Enter inside the code snippet.

The 4 msec delay in action

Here is the code that demonstrates the 4-msec delay - calling setTimeout recursively 100 times with a 0 delay.

As you can see by yourslef, the execution time for 100 iterations is around 400 msec.

You might need to reevaluate the snippet - for some reason the first evaluation takes much more time.

Press Ctrl-Enter inside the code snippet or modify the code and wait for 3 seconds…


function bar(iterations) {
  if(iterations === 0) {
    console.log("done in: " + (new Date() - startTimeout)+ " msec")
  }
  else {
  setTimeout(bar, 0, iterations - 1);
  }
  }

startTimeout = new Date();
console.log("Start");
bar(100);

Amazing. No?

The solution for fast timeouts

If we want to have a 0 msec timer, we have to use postMessage.

So, let’s do it and take the following code from setTimeout with a shorter delay.

setZeroTimeout takes a single argument: the callback function that is going to be called with 0 delay - but asynchronously.

(function() {
    var timeouts = [];
    var messageName = "zero-timeout-message";

    function setZeroTimeout(fn) {
        timeouts.push(fn);
        window.postMessage(messageName, "*");
    }

    function handleMessage(event) {
        if (event.source == window && event.data == messageName) {
            event.stopPropagation();
            if (timeouts.length > 0) {
                var fn = timeouts.shift();
                fn();
            }
        }
    }
    window.addEventListener("message", handleMessage, true);
    window.setZeroTimeout = setZeroTimeout;
})();

And now, let’s see it in action - in my browser, it takes around 17 msec for 100 iterations.

Again, you might need to reevaluate the snippet - as the first evaluation takes much more time.

Press Ctrl-Enter inside the code snippet or modify the code and wait for 3 seconds…


function runBaz(iterations) {
  function baz() {
    if(--iterations === 0) {
      console.log("done in: " + (new Date() - startZeroTimeout) + " msec")
    }
    else {
      setZeroTimeout(baz);
    }
  }
  baz();
}

startZeroTimeout = new Date();
console.log("OK");
runBaz(100);

By the way, what do you think about the interactive code snippets powered by KLIPSE?

You might what to give a star on Github