'Using a let in a for loop with setTimeout function [duplicate]
If the arguments to functions are passed by value in javascript how come the console.log() below seems to get its input by reference? Shouldn't each time it is invoked inside setTimeout just the value of i be passed and not its reference?
let i;
for (i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
Solution 1:[1]
Shouldn't each time it is invoked inside setTimeout just the value of i be passed and not its reference?
Yes, and this is exactly what is happening. It is passing the value of i by value to console.log(i). But, you're passing that value AFTER the entire for loop has completed when i has its terminal value because this is the order that things run:
- You declare
let i. - You start your
forloop. - The
forloop runs to completion, each iteration starting an additional timer. The timer is non-blocking and asynchronous so it just starts the timer and execution of the loop continues. - Then, sometime later (after the
forloop is completely done) with the value ofisitting on its terminal value, then the timers start firing which causes them to call their callback and then outputconsole.log(i).
Note, that if you made the let i be part of the for loop declaration, you would get completely different results:
for (let i = 1; i <= 5; i++) {
// Because let i was inside the for loop declaration,
// there is a separate copy of i for each iteration of the for loop here
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
Because here there is special treatment for the let i in the for loop. Each iteration of the loop gets it own separate copy of i and thus it still has the value it had when the loop iteration was run at the later time when the timer fires.
This is completely different than your original version where there is only one i for the entire loop and its value is incremented on each iteration and thus the value i had when each iteration of the loop ran is no longer available when the timer fires.
Solution 2:[2]
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
This has to do with a very important concept called scope. A scope is the piece of code where your variable exists.
In your example you define the variable before the for loop. So this happens internally:
- You create i
- i is set to 1
- i is referred to in your setTimeout() (5 times)
- i is incremented by 1 ( 5 times )
- i is logged 5 times, all as 6, because the values all refer to the same i
In my example i is scoped in the for loop. This means every iteration of the loop a new reference to i is generated. So these references refer to different variables from the computers point of view ( even tough your code names them all i ).
Solution 3:[3]
for (let i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
Solution 4:[4]
setTimeOut is an asynchronous function. It means it won't stop execution of other synchronous code. Each Console Log will wait 1 second to execute. But as the value of i has become 6 it will print that 5 times.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Rob Monhemius |
| Solution 3 | Sourabh Kumar |
| Solution 4 | ujjwal_mani |
