Reputation: 1661
Javascript, Event loop, setTimeout, IIFE, closure
Based on references below, my understanding of the following code is:
setTimeout() is non-blocking and handled by the Browser Web APIs, which put the callbacks on the callback queue when the timer is done. Then the event loop waits for the call stack to be free to run each callback in turn. setTimeout closure closes over the anonymous IIFE and has the correct value of index for each iteration.
for(var i = 0; i < 3; i++){
(function(index){
setTimeout(function(){
console.log(index);
}, 5000);
})(i);
console.log("loop="+i);
}
/*Output in console is
loop=0
loop=1
loop=2
//after 5 seconds
0
1
2
*/
I'm looking for an explanation of what's happening with the following code in Chrome.
for (var i = 0; i < 3; i++) {
setTimeout(
function(index) {
console.log(index);
}(i), 5000
);
console.log("loop="+i);
}
/* Output in console without any delay is:
0
loop=0
1
loop=1
2
loop=2
*/
Why is 'console.log(index)' executed immediately, without a 5 second delay?
How does the web API execute setTimeout() with a callback as an IIFE?
Are any callbacks put in the callback queue?
Does the event loop move any callbacks to the call stack?
Or is setTimeout() being ignored and its callback being executed immediately on the call stack?
References I've consulted:
Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014 https://www.youtube.com/watch?v=8aGhZQkoFbQ
Philip Roberts Help I'm stuck in an event loop 2016 https://www.youtube.com/watch?v=6MXRNXXgP_0
Call Stack & Event Loop https://www.youtube.com/watch?v=mk0lu9MKBto
JavaScript closure inside loops – simple practical example
Use IIFE in setTimeout in a loop, but why?
Upvotes: 2
Views: 470
Reputation: 14287
I'm looking for an explanation of what's happening with the following code in Chrome.
for (var i = 0; i < 3; i++) {
setTimeout(
function(index) {
console.log(index);
}(i), 5000
);
console.log("loop="+i);
}
/* Output in console without any delay is:
0
loop=0
1
loop=1
2
loop=2
*/
Consider the following statement:
function(index) {
console.log(index);
}(i)
This is an anonymous function and executed immediately (the parentheses '()' at the end executes the function): see the syntax function(param) {...}()
. So the effect is that for each iteration the above code is executed immediately.
The result is (as you see it):
0
1
2
The setTimeout at MDN. method expects a function (to be executed after the timer expires) or code as its first parameter. In this case you have code (not a function) that executes immediately. So, you see the result immediately.
The delay of 5 seconds has no effect, its never used. There is nothing to execute after the delay.
The effect is same in Firefox too.
You can try the code without the parentheses at the end of the anonymous function and see what happens:
function(index) {
console.log(index);
}
The function will execute after the delay of five seconds, in this case!
Upvotes: 0
Reputation: 846
In second example you are not passing the function to the setTimeout
but you rather passing it's result of the function call (in this case it's void).
function(index) {
console.log(index);
}(i)
you see, in this example your function invokes immediately thus, there's nothing to call later, and console logs in order.
Upvotes: 0
Reputation: 370699
In
setTimeout(
function(index) {
console.log(index);
}(i), 5000
);
You're invoking the first argument passed to setTimeout
immediately. When the interpreter comes across the setTimeout
line, it first tries to resolve all of its arguments to values. The first argument is a function invocation, so it invokes that function in the expectation that it will resolve to another function - just like how one could do
setTimeout(makeFn('foo'), 5000);
where makeFn
returns a function.
So, in your code, the
function(index) {
console.log(index);
}(i)
runs immediately, but it doesn't return anything - the interpreter resolves the setTimeout
line to
setTimeout(undefined, 5000);
but undefined
isn't a function, so nothing asynchronous gets queued up.
You don't have any IIFEs here - put the whole setTimeout
line in an IIFE instead:
for (var i = 0; i < 3; i++) {
((i) => {
setTimeout(
function() {
console.log(i);
}, 500
);
console.log("loop=" + i);
})(i);
}
(or, of course, use const
or let
instead of var
- best to avoid var
, its hoisting and function scope is very unintuitive and requires verbose workarounds like these in for
loops)
for (let i = 0; i < 3; i++) {
setTimeout(
function() {
console.log(i);
}, 500
);
console.log("loop=" + i);
}
Upvotes: 3