What is setTimeout doing when set to 0 milliseconds?
A few useful facts might help clarify what's happening:
- JavaScript is single-threaded. Asynchronous callbacks are assigned to a message placed in a message queue.
- When no code is currently executing, the event loop polls the message queue, requesting the next message in line to be processed (executed).
setTimeout
adds a message (with the callback provided) to the end of this queue after the specified delay has elapsed.
(Note: this means the delay in a setTimeout
call is not a sure thing; it is the minimum delay before the callback is executed. The actual time taken depends on how long it takes to process any messages ahead of it in the queue.)
So what happens if the delay is set to 0
? A new message is added to the queue immediately, and will be processed when the currently executing code is finished and any previously-added messages have been processed.
What's happening in your code
When you invoke setTimeout
…
setTimeout(function() {
console.log('AAA');
}, 0);
…a message gets added to the queue with the specified callback. The rest of your code…
for (i = 0; i < 1000; i++) {
console.log('BBB');
}
// etc.
…continues executing synchronously. Once it has completely finished, the event loop polls the message queue for the next message and finds the one with your setTimeout
callback, which is then processed (the callback is run).
The callback only ever gets executed after the currently executing code has finished, no matter how long that takes.
Further reading
For more details on the event loop, see:
- Concurrency model and event loop—MDN
- The JavaScript event loop: explained—Carbon Five
Why is setTimeout(fn, 0) sometimes useful?
In the question, there existed a race condition between:
- The browser's attempt to initialize the drop-down list, ready to have its selected index updated, and
- Your code to set the selected index
Your code was consistently winning this race and attempting to set drop-down selection before the browser was ready, meaning that the bug would appear.
This race existed because JavaScript has a single thread of execution that is shared with page rendering. In effect, running JavaScript blocks the updating of the DOM.
Your workaround was:
setTimeout(callback, 0)
Invoking setTimeout
with a callback, and zero as the second argument will schedule the callback to be run asynchronously, after the shortest possible delay - which will be around 10ms when the tab has focus and the JavaScript thread of execution is not busy.
The OP's solution, therefore was to delay by about 10ms, the setting of the selected index. This gave the browser an opportunity to initialize the DOM, fixing the bug.
Every version of Internet Explorer exhibited quirky behaviors and this kind of workaround was necessary at times. Alternatively it might have been a genuine bug in the OP's codebase.
See Philip Roberts talk "What the heck is the event loop?" for more thorough explanation.
Why is the `setTimeout` callback called after function execution, even if the delay is 0 ms?
When you create a promise, or call an async function, or set a timeout for 0 milliseconds, the function is immediately queued into the Javascript event loop. Essentially, the function is added to a queue of functions to call, and once the javascript interpreter has nothing to do it'll start calling those functions. So, when you set a timeout for 0 milliseconds, it queues the console.log("m")
, then calls the console.log("s")
, then it has nothing to do so it finishes the queued console.log("m")
, which is why it's out of order.
Why does a setTimeout delay of 0 still run after all other synchronous code in a for loop?
JavaScript, both in the browser and on the server, runs as an event loop. The runtime is constantly polling for events. Events consist of user actions (e.g. DOM element x was clicked), I/O (data came back from I/O or ajax call), and timer events (in this case). setTimeout(fn, 0)
simply adds an event to be processed by the event loop at minimum when 0 milliseconds have elapsed. It will always execute after the current event has been processed.
What is the difference between setTimeout(fn, 0) and setTimeout(fn, 1)?
I think the answer is "It depends" now.
We can run the code in different platform and browsers:
function setTimeouts() {
setTimeout(function() { console.log(2); }, 2);
setTimeout(function() { console.log(1); }, 1);
setTimeout(function() { console.log(0); }, 0);
}
for (var i = 0; i < 10; i++) {
setTimeouts();
}
Does the non-specified delay in setTimeout function means zero-delay?
As mentioned by the setTimeout
documentation, the delay
is an optional value that will be set to 0
if not specified.
delay
Optional
The time, in milliseconds (thousandths of a second), 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, as soon as possible. Note that in either case, the actual delay may be longer than intended; see Reasons for delays longer than specified below.
Related Topics
Pass Value to Iframe from a Window
Google Maps JavaScript API Referernotallowedmaperror
JavaScript If Statements Not Working
How to Understand Usecapture Parameter in Addeventlistener
Can't Throw Error from Within an Async Promise Executor Function
How to Submit a Form Using Phantomjs
Order of Hoisting in JavaScript
What Does ${} (Dollar Sign and Curly Braces) Mean in a String in JavaScript
Normalizing Mousewheel Speed Across Browsers
Scaling Socket.Io to Multiple Node.Js Processes Using Cluster
Firefox 'Cross-Origin Request Blocked' Despite Headers
How to Convert a Plain Object into an Es6 Map
How to Turn Nan from Parseint into 0 for an Empty String
Prototype: Deep Scope of "This" to Access Instance's Scope
Boolean Variable Returns as String from JavaScript Function