In the following scenario, event setTimeout get queued and only after stack clears out, i = 3 will print 3 times
//ver1
for (var i = 0; i < 3; i ++ ) setTimeout(()=> console.log(i), 3)
The following will print 1 2 3 in order since i is captured in func x ??
//ver2
for (var i = 0; i < 3; i++) {
(function x(i) {
setTimeout(() => {console.log(i)}, 30)
})(i);
}
Why the following only prints 2 one time?
//ver3
for (var i = 0; i < 3; i++) {
function x(i) {
setTimeout(() => {console.log(i)}, 30)
}(i);
}
I'm mostly confused between ver2 and ver3
EDIT:
ver2 explanation can be found here. Any insight on why ver3 only prints 2?
Your version 3 doesn't do at all what you think it does. Parenthesis are not optional on an IIFE. But because you named the function, it does get parsed as a function declaration:
for (var i = 0; i < 3; i++) {
function x(i) {
setTimeout(() => {console.log(i)}, 30)
}
(i);
}
You were never calling x(…) here. The (i) is just an unnecessarily-parenthesised expression evaluating the i variable. Your code executes the same as
for (var i = 0; i < 3; i++) {
i;
}
So why does this log 2 in the console? Because when you run this on the console, it prints the result value of the statement that was evaluated last: i, which had the value 2 in the last iteration of the loop.
Related
Why does javascript closure work differently in these examples?
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 1000);
}
// Output: 0, 1, 2
let i = 0;
for (i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 1000);
}
// Output: 3, 3, 3
In the first code example, you have three different i variables and in each iteration of the loop, callback function of setTimeout closes over a different copy of i variable.
In the second code example, there is only one i variable and in each iteration of the loop, callback function of setTimeout closes over the same i variable.
There is only one i variable in the second code example because you have declared it outside the loop; as a result, each iteration of the loop sees the same variable i.
In order to get the same output in the second code example, change it as shown below:
let i = 0;
for (i = 0; i < 3; i++) {
let j = i; // save a copy of the current value of 'i'
setTimeout(() => {
console.log(j); // closure over a different 'j' variable in each iteration
}, 1000);
}
I have a code sample
for(var i = 0; i < 3; i++) {
setTimeout() {
console.log("i " + i)
}
}
This is printing "3" to the console three times. However, I want it to print "0, 1, 2" without using let.
So far, I have tried the following -
var funcs = [];
function createfunc(i) {
return function() {
console.log("i ", i)
}
}
for(var i = 0; i < 3; i++) {
setTimeout(() => {
funcs[i] = createfunc(i) // statement 1
}, 1000)
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
This is working fine if I use the statement 1 just as it is without putting it into the setTimeout function. However, it is throwing an error if I'm using the setTimeout function. Can someone let me know how can I make print 0, 1, 2 to the console using the setTimeout function ?
It happens because of closure, anyway you can use the below codes, then you will be getting the proper result that you want.
for(var i = 0; i < 3; i++) {
((i) => {
setTimeout(() => {
console.log("i " + i)
}, 0);
})(i);
}
I'd like to understand the following code:
for (var i=0; i<3; i++){
setTimeout(() => {
console.log(i);
}, 1000);
}
console.log('After the Loop');
Prints After the loop,3,3,3
for (let i=0; i<3; i++){
setTimeout(() => {
console.log(i);
}, 1000);
}
console.log('After the Loop');
Prints After the loop,0,1,2
I know that the events are executed after the main, so the numbers of i appear after After the loop. And letmeans block scope (so iis visible in for-loop only) and varfunction scope (so it should be visible globally here), but I still don't see why the first prints 3,3,3.
Can anyone please provide a resonable explaination?
Thanks!
When you use var, the same i is shared by all the anonymous functions passed to setTimeout(). The timeouts fire a relatively long time after the loop finishes, so by that time the value of the (shared) i is 3.
Consider the following examples which show i defined using var and let and how scope is determined at the function and block level.
// "i" defined with "var" within the function scope
function foo() {
var i;
for (i = 0; i < 3; i++) {
setTimeout(() => console.log('foo', i), 250);
}
// output: 3 3 3
}
// "i" defined with "let" within the function scope
function bar() {
let i;
for (i = 0; i < 3; i++) {
setTimeout(() => console.log('bar', i), 250);
}
// output: 3 3 3
}
// "i" is defined within the block however it gets hoisted up
// to the function scope as this is the behavior of "var"
function baz() {
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log('baz', i), 250);
}
// output: 3 3 3
}
// "i" is defined within the block and "let" allows "i" to maintain the block scope
function boo() {
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log('boo', i), 250);
}
// output: 0 1 2
}
Here is the code
function fn(){
for (var i = 0; i < 4; i++) {
var tc=setTimeout(function(i){
console.log(i)
clearTimeout(tc)
},10,i);
}
}
fn()
//0, 1, 2 - output
I can't understand why the output is not '0, 1, 2, 3',only output three times.
Even more stranger is below
When I change the loop times, now, I call loop times as T.
T: 1 ---> 0
T: 2 ---> 0
T: 3 ---> 0, 1
above, the right is the output.
Here you go. Print i out at interval T.
function fn(){
var T = 1000;
var func = function(i,len) {
if ( i < len ) {
console.log(i);
setTimeout(func,T,i+1,len); // Calling itself.
}
};
setTimeout(func,T,0,4);
}
fn();
At last got it...The problem is with closure..You just need to pass the variable tc to setTimeout function.
function fn(){
for (var i = 0; i < 4; i++) {
var tc=setTimeout(function(i,tc){
console.log(i)
clearTimeout(tc);
},10,i,tc);
}
}
fn()
What you are doing is that you are overwriting the value of tc in the for loop.
By overwriting, you lose handle to corresponding return value from each call to setTimeout().
You need to clear individual tc as you go in the for loop.
So, something like this would work.
Note that I have hard-coded, tc array but you can make it generic, so this example is just for a reference as to how you could get 0,1,2,3.
var tc = [0,0,0,0];
function fn(){
for (var i = 0; i < 4; i++) {
tc[i]=setTimeout(function(i){
console.log(i);
clearTimeout(tc[i]);
},10,i);
}
}
fn();
A common pitfall with JavaScript closures is running setTimeout() from a for loop, and expecting the counter to be passed with different values at each iteration, while in practice it gets assigned the last value before the setTimeout() functions execute:
for (i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i)
}, 100);
} // => prints "10" 10 times
One solution to this is to have an Immediately Invoked Function Expression:
for (i = 0; i < 10; i++)
(function(j) {
setTimeout(function foo() {
console.log(j)
}, 100);
})(i); // prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Another is to pass an extra callback argument to setTimeout() (which doesn't work in IE<9):
for (i = 0; i < 10; i++) {
setTimeout(function foo(n) {
console.log(n)
}, 100, i);
}
But why does the following, simplest, code, produce the same result (0, 1, 2, ... 9)?
for (var i = 0; i < 10; i++)
setTimeout(console.log(i), 100);
This apparently surprising behavior occurs because the first parameter to setTimeout can be a function as well as a string, the latter being eval()-ed as code.
So setTimeout(console.log(i), 100); will execute console.log(i) right away, which returns undefined. Then setTimeout("", 100) will be executed, with a NOP call after 100ms (or optimized away by the engine).
Just for grins, another thing you can do (when you've got .bind()) is
for (i = 0; i < 10; i++) {
setTimeout(function () {
var i = +this;
console.log(i)
}.bind(i), 100);
}
A little bit less of a mess than an IIFE.
why does the following, simplest, code, produce the same result (0, 1,
2, ... 9)?
for (var i = 0; i < 10; i++)
setTimeout(console.log(i), 100);
Because it actually doesn't. If you look closely, you will notice that the log messages will not need the tenth of a second before they appear in console. By calling the console.log(i) right away, you are only passing the result of the call (undefined) to setTimeout, which will do nothing later. In fact, the code is equivalent to
for (var i = 0; i < 10; i++) {
console.log(i);
setTimeout(undefined, 100);
}
You will notice the difference better if you replace the 100 by i*500 in all your snippets, so that the log messages should be delayed at an interval of a half second.