I'm trying to use setTimeout to execute an anonymous function that I pass information into and I'm having trouble. This (hard-coded version) would work just fine:
setTimeout(function(){alert("hello");},1000);
setTimeout(function(){alert("world");},2000);
But I'm trying to take the hello and world from an array and pass them into the function without (a) using global variables, and (2) using eval. I know how I could do it using globals or eval, but how can I do it without. Here is what I'd like to do (but I know it won't work):
var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
setTimeout( function(){alert(strings[i]);}, delay);
delay += 1000;
}
Of course strings[i] will be out of context. How can I pass strings[i] into that anonymous function without eval or globals?
The setTimeout function callback isn't triggered until the for loop execution has completed. When the for loop has finished executing the value of i is 5. Now when the setTimeout call begins to execute it uses the last set value of i which is 5. Hence 5 is printed in all the setTimeout callbacks.
First of all, the setTimeout JavaScript method should contain the function that you intend to apply. The second parameter sets a time when the specified function is to be called. However, it is optional, and the default value is 0.
setTimeout is a guarantee to a minimum time of execution. I wrote two gists that you can copy and paste into the console to check this.
setTimeout allows us to run a function once after the interval of time. setInterval allows us to run a function repeatedly, starting after the interval of time, then repeating continuously at that interval.
This is the very frequently repeated "how do I use a loop variable in a closure" problem.
The canonical solution is to call a function which returns a function that's bound to the current value of the loop variable:
var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(strings[i]), delay);
delay += 1000;
}
The outer definition function(s) { ... }
creates a new scope where s
is bound to the current value of the supplied parameter - i.e. strings[i]
- where it's available to the inner scope.
Just add a scope around the setTimeout call:
var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
(function(s){
setTimeout( function(){alert(s);}, delay);
})(strings[i]);
delay += 1000;
}
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