for (var i=1; i<=5; i++) {
(function(){
setTimeout( function timer(){
console.log( i );
}, i*1000 );
})();
}
I am confused!
Why this won't work?
I think that engine goes line by line and executes the code line by line after compiling it and in this case the engine will go into the loop then execute all the code in which case the output must be like :-
0
1
2
3
4
Why it is not waiting for 1000 ms? How for loop is iterating ahead while the function setTimeout is not yet executed?
The for
loop is executing the IIFE five times.
In it, setTimeout
is building a future stack of timer
functions.
Deeper in it, each timer
function closes over the i
variable, by reference.
The for
loop takes less then a second to execute. Hence, every timer
functions execution from the future stack occurs after the loop has finished, each referencing the post-for i
value: 6
.
At the end of the for
loop, referencing i
is equal with getting the number 6
value. That's why i
must be passed by value to future timer
functions, during the loop.
Deeper in it, each timer function closes over the i variable by reference
Nope. :) This is a classic case of getting the right answer with the wrong reason. Reference vs value doesn't come into play with closure. Orthogonal. Moreover, a simple value primitive like a number would always be by-value.
The reason the IIFE as shown doesn't fix anything is because there's still only one i
being closed over. If the IIFE has its own i
then each timeout callback closes over a diff i
thereby preserving the value in each iteration. :)
In the next example, the working example, you pass i
to the IIFE. i
being a primitive value, you pass it by value. The parameter-value j
is then bounded to each IIFE scope, indiferent to i
values changing the next loop steps over, and is being closed over by a timer
function in each IIFE. By reference. Or not? :) Thanks for taking the time! By value, you are right.
for (var i=1; i<=5; i++) {
(function(j){
setTimeout( function timer(){
console.log( j );
}, j*1000 );
})( i );
}
In the end, the five IIFE stack produces another five timer
function stack. What's different is the inner scope of each IIFE, accessible to their own respective inner timer
functions. In the first example, the inner scope of each IIFE holds public i
, with the value of 6
. In the second one, the scope of each IIFE holds private and different j
values, thus allowing their respective timer
functions to close over different values.
To summarize, we are building different and private IIFE scopes, for each inner timer
function to close over, by passing the i
value as the j
parameter, different and private to each outer holding IIFE. If not, by the time they execute, each timer
function in the future stack closes over the same public outer scope, where the for
loop has finished execution, leaving i
to holding the value 6
. I hope I got it right this time? :)
Got it thanks @getify and @itmitica. I was also thinking like @itmitica , that all i
's are referencing to the same variable so 6 is printed out 5 times. Closures go against my intuition. :D
The for loop takes less then a second to execute. Hence, every timer functions execution from the future stack occurs after the loop has finished.
I believe this to be wrong as the timed code will not be executed before the loop is finished, even if the loop takes more than a second because the stack will be stuck at the function/script containing the loop.
the stack will be stuck at the function/script containing the loop.
@oldergod could you please go into the details of this, I am not able to get it.
See: https://youtu.be/8aGhZQkoFbQ?t=771
After one second, the browser will indeed add the timed code to the task queue but then, this new task can be executed only when the stack gets empty. The stack can be empty only if the loop has finished .
No matter how long a piece of code works, it won't be "interrupted" by a shorter timeout being ready to fire. The event loop is where things are scheduled asynchronously and every item gets in line behind everything else waiting. Timeouts will always happen at a minimum on the next event loop tick, never the current one.
Thanks now I finally a hold on this thing!
I have not gone through the Async part of the book series so I didn't know the details of it. But now I got it.
I obviously didn't really knew what I was talking about, so I took the time to properly learn it.
Here it is JS Slo-Mo: for loop, closure, async callbacks.
Thanks @getify and @oldergod !
Most helpful comment
I obviously didn't really knew what I was talking about, so I took the time to properly learn it.
Here it is JS Slo-Mo: for loop, closure, async callbacks.
Thanks @getify and @oldergod !