Is setTimeout with no delay the same as executing the function instantly?

JavascriptDelaySettimeoutTiming

Javascript Problem Overview


I am looking at some existing code in a web application. I saw this:

window.setTimeout(function () { ... })

Is this the same as just executing the function content right away?

Javascript Solutions


Solution 1 - Javascript

It won't necessarily run right away, neither will explicitly setting the delay to 0. The reason is that setTimeout removes the function from the execution queue and it will only be invoked after JavaScript has finished with the current execution queue.

console.log(1);
setTimeout(function() {console.log(2)});
console.log(3);
console.log(4);
console.log(5);
//console logs 1,3,4,5,2

for more details see http://javascriptweblog.wordpress.com/2010/06/28/understanding-javascript-timers/

Solution 2 - Javascript

There is a minimum delay that setTimeout uses (4ms as per HTML5, Firefox 3.6 uses 10ms). There is a discussion about it on the Mozilla Developer Center documentation page.

Solution 3 - Javascript

The short answer is no it is not the same.

The description for the delay parameter in the MDN setTimeout documentation is:

> The time, in milliseconds that the timer should wait before the > specified function or code is executed. If this parameter is omitted, > a value of 0 is used, meaning execute "immediately", or more > accurately, the next event cycle. Note that in either case, the actual > delay may be longer than intended; see Reasons for delays longer than > specified below.

Omitting the delay or using a value of 0 will execute in the next event cycle however it may take longer than that. This is the first reason why it is different than executing the function content straight away. For example:

document.getElementById("runNow").addEventListener("click", runNow);
document.getElementById("runNoDelay").addEventListener("click", runNoDelay);

function runNow() {
  clearLog();
  addLog("Start");
  addLog("Hello");
  addLog("End");
}

function runNoDelay() {
  clearLog();
  addLog("Start");
  setTimeout(function() {
    addLog("Hello");
  });
  addLog("End");
}

function clearLog() {
  const log = document.getElementById("log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }
}

function addLog(message) {
  const newLine = document.createElement("pre");
  newLine.textContent = message;
  document.getElementById("log").appendChild(newLine);
}

<button id="runNow">Run Now</button>
<button id="runNoDelay">Run With No Delay</button>
<div id="log"></div>

Notice how when using setTimeout with no delay the message "End" occurs before "Hello".

Even when the delay is omitted or is 0 there are reasons why it may be longer which may include:

  • "... browsers will enforce a minimum timeout of 4 milliseconds once a nested call to setTimeout has been scheduled 5 times.".
  • The browser tab is inactive.
  • The script has been recognised as a tracking script and is being throttled.
  • Other tasks in the queue took longer than the delay to execute.
  • The browser decides to use some other implementation-defined length of time for whatever reason.

And example of this is:

document.getElementById("run").addEventListener("click", run);

function run() {
  clearLog();
  const now = new Date().getMilliseconds();
  setTimeout(function() {
    timeout(0, now);
  });
}

function clearLog() {
  const log = document.getElementById("log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }
}

function timeout(nestingLevel, last) {
  const now = new Date().getMilliseconds();
  logline(nestingLevel, now, last);
  if (nestingLevel < 9) {
    setTimeout(function() {
      timeout(nestingLevel + 1, now);
    });
  }
}

function logline(nestingLevel, now, last) {
  const newLine = document.createElement("pre");
  newLine.textContent = `${nestingLevel}                ${now - last}`;
  document.getElementById("log").appendChild(newLine);
}

<button id="run">Run</button>
<pre>nesting level    delay</pre>
<div id="log"></div>

(Adapted from the example on MDN).

Notice how the delay is 0 (or close to 0) until the nesting level reaches a certain point.

It's also important to note that setTimeout is being called with a wrapper function (function () { ... }) which means that the value for this will be the same as if the contents of the function were executed right away.


Chrome (version 92.0.4515.131) and Firefox (version 91.0) enforce a minimum timeout of 4 milliseconds when the nesting level is 4 as shown in the example above.

The HTML Standard says "If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4".

My interpretation is that these browsers are enforcing this minimum timeout 2 tasks too early. It should be applying it to the scheduling of the 7th task where the nesting level is 6 (greater than 5).

At the end of the day the browser can use some other implementation-defined length of time if it wants so it is an interesting but moot point.

Solution 4 - Javascript

You are missing the millisecond parameter...

setTimeout(function() { /*something*/ }, 0);

The 0 sets the delay to 0 but what it actually does is to let your function "jump the queue" of the browser execution list. The browser has a bunch of things to do such as rendering objects on the page, and by calling this, your function will run as soon as the browser has some cycles.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAishwarView Question on Stackoverflow
Solution 1 - JavascriptangusCView Answer on Stackoverflow
Solution 2 - JavascriptDaniel VandersluisView Answer on Stackoverflow
Solution 3 - JavascriptsteinybotView Answer on Stackoverflow
Solution 4 - JavascriptGaryView Answer on Stackoverflow