Reputation: 9724
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?
Upvotes: 59
Views: 29762
Reputation: 6164
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:
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.
Upvotes: 3
Reputation: 94254
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.
Upvotes: 21
Reputation: 1084
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/
Upvotes: 105
Reputation: 1007
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.
Upvotes: -2