I have a very trivial question. For a simple loop with setTimeout, like this:
for (var count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
console gives an output like this:
Count = 3
Count = 3
Count = 3
Not sure why the output like this. Anyone could explain, please?
This has to do with how scoping and hoisting is being treated in JavaScript.
What happens in your code is that the JS engine modifies your code to this:
var count;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + count);
}, 1000 * count);
}
And when setTimeout()
is being run it will first look in it's own scope after count
but it won't find it so then it'll start looking in the functions that closes (this is called closures) over the setTimeout
function until it finds the var count
statement, which will have the value 3 since loop will have finished before the first timeout function has been executed.
More code-ily explained your code actually looks like this:
//first iteration
var count = 0; //this is 1 because of count++ in your for loop.
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + 1);
}, 1000 * 1);
}
count = count + 1; //count = 1
//second iteration
var count = 1;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + 2);
}, 1000 * 2);
}
count = count + 1; //count = 2
//third iteration
var count = 2;
for (count = 0; count < 3; count++) {
setTimeout(function() {
alert("Count = " + 3);
}, 1000 * 3);
}
count = count + 1; //count = 3
//after 1000 ms
window.setTimeout(alert(count));
//after 2000 ms
window.setTimeout(alert(count));
//after 3000 ms
window.setTimeout(alert(count));
think about it like that:
AFTER the 1000*n miliseconds are over, what will be the value of count?
of course it will be 3, because the foor loop ended way earlier than the timeout of 1000*n ms.
in order to print 1,2,3 you'll need the following:
for (var count = 0; count < 3; count++) {
do_alert(num);
}
function do_alert(num) {
setTimeout(function() {
alert("Count = " + num);
}, 1000 * num);
}
a different approach is to make it a closure function
(explained well in JavaScript closures vs. anonymous functions)
for (var count = 0; count < 3; count++) {
(function(num){setTimeout(function() {
alert("Count = " + num);
}, 1000 * num)})(count);
}
these two code samples will actually work similarly.
the first sample calls a named function (do_alert) each iteration.
the second sample calls a CLOSURE anonymous function (which is just like do_alert
) each iteration.
it's all a matter of SCOPE.
hope that helps.
This is to do with closure scoping. The same variable count
is available in the scope for each of the setTimeout callback functions. You are incrementing its value and creating a function, but each instance of the function has the same variable count
in its scope, and by the time the callback functions execute it will have the value 3.
You need to create a copy of the variable (e.g. var localCount = count
) inside a new scope in the for
loop to make this work. As for
doesn't create a scope (which is the cause of the whole thing) you need to introduce one with a function scope.
e.g.
for (var i = 0; i < 5; i++) {
(function() {
var j = i;
setTimeout(function() {
console.log(j)
},
j*100);
})();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With