Passing values to a function in javascript when using setTimeout [duplicate] - javascript

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
I am trying to set several timeouts inside a loop where the parameter function of setTimeout uses diferent values (depending of the loop index) as parameter. This is a simplificated example:
for(i=0; i<5; i++)
{
var m = setTimeout( function () {console.log(i)}, (i+1)*2000 );
}
I thought that with the code above I get "0, 1, 2, 3, 4" every 2 seconds. Instead of this I get "5, 5, 5, 5, 5" every 2 seconds. Why?

If you're happy to restrict support to modern web browsers (i.e. not IE 9 and earlier) the below will work:
for(i=0; i<5; i++)
{
var m = setTimeout( function (i) {console.log(i)}, (i+1)*2000, i );
}
You can pass your variable as a third argument to setTimeout and then receive it in your setTimeout function.
As for why your original code doesn't work, it has to do with Javascript scope which is explained quite well here: What is lexical scope?

You need a wrapper function to create a closure and keep value of i at the moment of iteration:
for(i=0; i<5; i++) {
(function(timeout) {
var m = setTimeout( function () {console.log(timeout)}, (timeout+1)*2000 );
})(i)
}

Related

I get strange output when i use var keyword in for loop with setTimeOut inside [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
I have this code.
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
I don't understand the output from this lines of code:
The output in the console is number 6,and it says that is repeated five times.
If i use the let keyword for "i" then i get the output that i expect,
1,2,3,4,5 after one second
Why is that ?
var has scoping issues and that's why let was introduced.
In your for loop you are defining i, but actually it's stuck to the global scope, and after 1 second, the for loop would actually be done, and when the setTimeout callback is fired, i would have already reached 6 and it's read from the global scope.
In a nutshell, because i is stuck to the upper scope of for, each iteration modifies the calue of i and doesn't create another i.
If you change var to let, the issue is resolved.
setTimeout is async hence will execute after the loop is done, that s why you have i=6, put it in a self invoking function to retain the value of i or use let instead of var in your loop
for (var i = 1; i <= 5; i++) {
((i) => setTimeout(function() {
console.log(i);
}, 1000))(i)
}
for (let i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}

Use IIFE in setTimeout in a loop, but why? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
I know it is a classic js question:
(My question is not how to solve this problem, but how IIFE solve this problem. Thanks for the other answer link but I didn't find the answer I want)
for(var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
},1000)
}
This will print out five consecutive 5, and one way to avoid that is to create IIFE in setTimeout, I know it creates a closure but still why? Can someone give a more specific explanation about it?
Also why can't I just pass a parameter to the function?
for(var i = 0; i < 5; i++) {
setTimeout(function(i) {
console.log(i);
},1000)
}
This prints out 5 undefined...I got more confused, why is that?
If I understand your question correctly, you want to print 0...4. You can achieve that by using let instead of var which creates a new binding of i for each loop iteration [1], [2]:
// Prints 0, 1, 2, 3, 4:
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Your suggestion to add an argument i to the callback fails as the calling function setTimeout doesn't pass anything to the callback. Thus the i argument is undefined.
Alternatively, use the classic IIFE approach:
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
Even better, of course, would be to move the for-loop into the setTimeout callback. But I assume you chose this code for demonstration purposes only.

setTimeout within a for loop [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Calling functions with setTimeout()
(6 answers)
Closed 6 years ago.
I am trying to set a delay in the for loop in javascript. I want it to log i, have a delay, then log i, and so on. My question is, why does the following code work, having a function return a function as can be seen below:
for (var i = 1; i <= 5; i++) {
var tick = function(i) {
return function() {
console.log(i);
}
};
setTimeout(tick(i), 500 * i);
}
And the following code not work as expected:
for (var i = 1; i <= 5; i++) {
var tick = function(i) {
return console.log(i);
};
setTimeout(tick(i), 500 * i);
}
It prints out all the values in the for loop at once. Could someone please explain why this happens?
In both pieces of code, tick(i) is executed immediately in each loop
The difference is, that in the first example, tick(i) returns a function, which is called by setTimeout, and that outputs to the console
In the second example, the console.log is called immediately, undefined is returned, and setTimeout does nothing once the timeout has triggered as the given argument is not a function (or a string to be eval'd)

setTimeout not working as expected [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I'm writing a for loop in Javascript. The desired goal is to print out 0, 1, 2 with a 3 second gap in between.
for (var i=0; i<3; i++) {
console.log(i);
}
This prints everything out as expected, with no pause. But when I add in a setTimeout:
for (var i=0; i<3; i++) {
setTimeout(function() {console.log{i},3000*i}
}
The result is that it prints out 3, 3, 3 with a 3 second gap. The pause worked, but it looks like its completing the loop before the right numbers can get printed.
You're exactly right that the loop is getting completed before the setTimeout calls run. Since all of your timeout functions reference i, they're all going to print out 3. The way to fix this is to capture the value of i in a closure.
for (var i = 0; i < 3; i++) {
(function(index) {
setTimeout(function() {
console.log(index);
}, 3000 * index);
})(i); // Instantly call the function and pass the value of i
}

Why does this code return undefined 4 times instead of the array values? [duplicate]

This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
This is an example from a blog post by Avaylo Gerchev that addresses IIFE's. The following block of code returns 4 repeated 'undefined' responses by design:
function printFruits(fruits){
for (var i = 0; i < fruits.length; i++) {
setTimeout( function(){
console.log( fruits[i] );
}, i * 1000 );
}
}
printFruits(["Lemon", "Orange", "Mango", "Banana"]);
Avaylo then shows how to produce what (to me) would have been the expected output of the first code block (it outputs the values of the array):
function printFruits(fruits){
for (var i = 0; i < fruits.length; i++) {
(function(){
var current = i; // define new variable that will hold the current value of "i"
setTimeout( function(){
console.log( fruits[current] ); // this time the value of "current" will be different for each iteration
}, current * 1000 );
})();
}
}
printFruits(["Lemon", "Orange", "Mango", "Banana"]);
I understand that the IIFE creates a new scope. What I don't understand is the reason why the first block of code doesn't produce the (again, to me) expected output of returning the values in the array. I've been staring at this for a few days now, thus I conclude that my javascript knowledge is missing something fundamental.
Thank you for any insight!
In the first code block, the variable "i" is not accessible by the closure because setTimeout only defines what code to run but doesn't actually run it on definition, only later.
Having the closure there and running it immediately (with the parentheses () at the end) is a way to save the value of "i" in each iteration so that when setTimeout runs, it can access it.

Categories