I have this script:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
But 3
is alerted both times, instead of 1
then 2
.
Is there a way to pass i
, without writing the function as a string?
I have this script:
for (var i = 1; i <= 2; i++) {
setTimeout(function() { alert(i) }, 100);
}
But 3
is alerted both times, instead of 1
then 2
.
Is there a way to pass i
, without writing the function as a string?
You have to arrange for a distinct copy of "i" to be present for each of the timeout functions.
function doSetTimeout(i) {
setTimeout(function() {
alert(i);
}, 100);
}
for (var i = 1; i <= 2; ++i)
doSetTimeout(i);
If you don't do something like this (and there are other variations on this same idea), then each of the timer handler functions will share the same variable "i". When the loop is finished, what's the value of "i"? It's 3! By using an intermediating function, a copy of the value of the variable is made. Since the timeout handler is created in the context of that copy, it has its own private "i" to use.
Edit:
There have been a couple of comments over time in which some confusion was evident over the fact that setting up a few timeouts causes the handlers to all fire at the same time. It's important to understand that the process of setting up the timer — the calls to
setTimeout()
— take almost no time at all. That is, telling the system, "Please call this function after 1000 milliseconds" will return almost immediately, as the process of installing the timeout request in the timer queue is very fast.Thus, if a succession of timeout requests is made, as is the case in the code in the OP and in my answer, and the time delay value is the same for each one, then once that amount of time has elapsed all the timer handlers will be called one after another in rapid succession.
If what you need is for the handlers to be called at intervals, you can either use
setInterval()
, which is called exactly likesetTimeout()
but which will fire more than once after repeated delays of the requested amount, or instead you can establish the timeouts and multiply the time value by your iteration counter. That is, to modify my example code:function doScaledTimeout(i) { setTimeout(function() { alert(I); }, i * 5000); }
(With a
100
millisecond timeout, the effect won't be very obvious, so I bumped the number up to 5000.) The value ofi
is multiplied by the base delay value, so calling that 5 times in a loop will result in delays of 5 seconds, 10 seconds, 15 seconds, 20 seconds, and 25 seconds.
Update
Here in 2018, there is a simpler alternative. With the new ability to declare variables in scopes more narrow than functions, the original code would work if so modified:
for (let i = 1; i <= 2; i++) {
setTimeout(function() {
alert(i)
}, 100);
}
The let
declaration, unlike var
, will itself cause there to be a distinct i
for each iteration of the loop.
for
loop runs, all of the timers will fire. –
Stent for
loop is to start 5 timers that will all fire 5 seconds after that point, again essentially at the same time. If you want the timers to fire at 5 second intervals, you could use an interval timer, or else multiply 5000 by the value of i
for the timeout duration. –
Stent for
loop completes. A setTimeout()
call always defers execution of the function, even if the timeout is zero seconds. –
Stent let a = ['one', 'two', 'three'], i = 0; let timer = setInterval(() => { console.log(a[i]); i++; if( i >= a.length ) { clearInterval(timer); }}, 1000);
Copy and paste that into your console to try it. –
Hotchpotch You can use an immediately-invoked function expression (IIFE) to create a closure around setTimeout
:
for (var i = 1; i <= 3; i++) {
(function(index) {
setTimeout(function() { alert(index); }, i * 1000);
})(i);
}
alert(i)
from an anonymous callback. The problem was closure references i
from global for
block. So the proper answer is: IIFE creates additional scope per iteration to bound i
and pass it to the anonymous callback. Then closure references i
from local iteration scope. –
Michaelamichaele let
instead of var
in for loop –
Cummerbund This's Because!
the Solution's declaring a single scope for each iteration by using a self-function executed(anonymous one or better IIFE) and having a copy of i in it, like this:
for (var i = 1; i <= 2; i++) {
(function(){
var j = i;
setTimeout(function() { console.log(j) }, 100);
})();
}
the cleaner one would be
for (var i = 1; i <= 2; i++) {
(function(i){
setTimeout(function() { console.log(i) }, 100);
})(i);
}
The use of an IIFE(self-executed function) inside each iteration created a new scope for each iteration, which gave our timeout function callbacks the opportunity to close over a new scope for each iteration, one which had a variable with the right per-iteration value in it for us to access.
The function argument to setTimeout
is closing over the loop variable. The loop finishes before the first timeout and displays the current value of i
, which is 3
.
Because JavaScript variables only have function scope, the solution is to pass the loop variable to a function that sets the timeout. You can declare and call such a function like this:
for (var i = 1; i <= 2; i++) {
(function (x) {
setTimeout(function () { alert(x); }, 100);
})(i);
}
i * 1000
–
Plume You can use the extra arguments to setTimeout to pass parameters to the callback function.
for (var i = 1; i <= 2; i++) {
setTimeout(function(j) { alert(j) }, 100, i);
}
Note: This doesn't work on IE9 and below browsers.
ANSWER?
I'm using it for an animation for adding items to a cart - a cart icon floats to the cart area from the product "add" button, when clicked:
function addCartItem(opts) {
for (var i=0; i<opts.qty; i++) {
setTimeout(function() {
console.log('ADDED ONE!');
}, 1000*i);
}
};
NOTE the duration is in unit times n epocs.
So starting at the the click moment, the animations start epoc (of EACH animation) is the product of each one-second-unit multiplied by the number of items.
epoc: https://en.wikipedia.org/wiki/Epoch_(reference_date)
Hope this helps!
You could use bind
method
for (var i = 1, j = 1; i <= 3; i++, j++) {
setTimeout(function() {
alert(this);
}.bind(i), j * 100);
}
this
. But point taken. similar to this you could this too setTimeout(console.log.bind(console,i), 1000);
–
Bide Well, another working solution based on Cody's answer but a little more general can be something like this:
function timedAlert(msg, timing){
setTimeout(function(){
alert(msg);
}, timing);
}
function yourFunction(time, counter){
for (var i = 1; i <= counter; i++) {
var msg = i, timing = i * time * 1000; //this is in seconds
timedAlert (msg, timing);
};
}
yourFunction(timeInSeconds, counter); // well here are the values of your choice.
I had the same problem once this is how I solved it.
Suppose I want 12 delays with an interval of 2 secs
function animate(i){
myVar=setTimeout(function(){
alert(i);
if(i==12){
clearTimeout(myVar);
return;
}
animate(i+1)
},2000)
}
var i=1; //i is the start point 1 to 12 that is
animate(i); //1,2,3,4..12 will be alerted with 2 sec delay
animate(i);
- i
is undefined, this whole method will only work as described if you call animinate(1)
, will not work correctly with any other value. The parameter is pointless at best. –
Condescendence the real solution is here, but you need to be familiar with PHP programing language. you must mix PHP and JAVASCRIPT orders in order to reach to your purpose.
pay attention to this :
<?php
for($i=1;$i<=3;$i++){
echo "<script language='javascript' >
setTimeout(function(){alert('".$i."');},3000);
</script>";
}
?>
It exactly does what you want, but be careful about how to make ralation between PHP variables and JAVASCRIPT ones.
© 2022 - 2024 — McMap. All rights reserved.
setTimeout
s is NOT the best way to do it. – Ciaphalet
instead ofvar
.This will solve your problem – Subpoena