Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 7 years ago.
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 like setTimeout() 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 of i 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.
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);
}
This's Because!
The timeout function
callbacks are all running well after the completion of the loop. In fact,
as timers go, even if it was setTimeout(.., 0) on each iteration, all
those function callbacks would still run strictly after the completion
of the loop, that's why 3 was reflected!
all two of those functions, though they are defined
separately in each loop iteration, are closed over the same shared global
scope, which has, in fact, only one i in it.
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);
}
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);
}
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
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.
This question already has answers here:
How to terminate endless while loop via setTimeout in Javascript
(3 answers)
Closed 2 years ago.
The following code is work as the following
while loop>> bring a and b values>> settimeout>> check the value by if-condition>> continue it true, else break;
This code with settimeout and is not working:
var number = 0;
start_position: while (true) {
setTimeout(function() {
console.log("Anything you want to print");
var a = Math.random();
var b = Math.random();
}, 1000)
number++;
if (a > b) continue start_position;
break;
}
This code without settimeout and is work just fine:
var number = 0;
start_position: while (true) {
console.log("Anything you want to print");
var a = Math.random();
var b = Math.random();
number++;
if (a > b) continue start_position;
break;
}
This is another way I tried too:
var counter = 0;
var i = setInterval(function(){
var a=Math.random();
var b=Math.random();
console.log("a:",a);
console.log("b:",b);
counter++;
if(a<b || a=b) {
clearInterval(i);
}
}, 200);
Please, any suggestions?
Edit: This is more along the lines of what you want to accomplish. In your original case you had two problems A) The variables that you are checking are not in the correct function scopes and B) setTimeout functions get pushed into an event loop in javascript. Javascript will only run functions pushed into an event loop when the main thread is clear since javascript is single threaded. However because you have a while loop, that while loop will continue to run until it hits the break condition, never allowing your timeout function to be run, so it the while will run forever and the timeout function will never get run. This solution wraps the whole thing in a setInterval instead, setInterval will run periodically and then in this case is will clear itself when your condition is met.
var number = 0;
var interval;
function clear() {
if (interval !== undefined) {
clearInterval(interval);
}
callFunctionYouWantToMoveTo();
}
interval = setInterval(function() {
console.log("Anything you want to print");
a=Math.random();
b=Math.random();
number++;
if (a>b) return;
clear();
}, 1000)
I am trying to loop through an array, but want to output each value of the array with a delay. This is what my current understanding is on how it should work:
EDIT
Requested JS Fiddle: http://jsfiddle.net/d3whkjww/
loopThroughSplittedText: function(splittedText) {
for (var i = 0; i < splittedText.length; i++) {
// for each iteration console.log a word
// and make a pause after it
setTimeout(
console.log(splittedText[i]),
1000
);
};
},
Yet, it does not work, and I believe it might be, because the arguments in the "for" loop have to be inside the setTimeout function. Yet I don't know how to make it work.
All I get is every value of the array at once, but I want them appear with a delay. How do I do that?
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
function loopThroughSplittedText(splittedText) {
for (var i = 0; i < splittedText.length; i++) {
// for each iteration console.log a word
// and make a pause after it
(function (i) {
setTimeout(function () {
document.getElementById('text').innerHTML += splittedText[i];
console.log(splittedText[i]);
}, 1000 * i);
})(i);
};
}
loopThroughSplittedText(splittedText);
Fiddle Demo
Chances are you're going to want to use a recursive function instead of a for loop here. However, I'll explain both ways just in case you (or someone else reading this) has your heart set on doing this with a loop.
For a recursive function, the general idea is that you'll want to call the function once, then let it call itself repeatedly until it's finished doing what you want it to do. In terms of code, it will could look something a bit like this:
loopThroughSplittedText: function(splittedText) {
// Create our counter; delayedOutput will use this to
// track how far along in our string we are currently at
var locationInString = 0;
function delayedOutput() {
// Output the next letter in our string
console.log(splittedText[locationInString]);
// Increment our counter so that on the next call we are on the next letter
locationInString++;
// Only perform setTimeout if we still have text left to output
if (locationInString < splittedText.length) {
// Functions can reference themselves using their own name
setTimeout(delayedOutput, 1000);
}
}
// Call our function once to get things started
delayedOutput();
},
Alternatively, if you really prefer using a loop, you can still do it, but there's a fair bit of fiddling that has to be done to accomplish this.
First, you're going to need to place console.log within its own function. This is because when you place console.log(something), you're not actually passing it, but calling it right then and there, which is not what you want; by calling it, it spits out the text to the console right away rather than waiting until later. Tucking it away in its own function allows it to be passed to setTimeout so it can be called later on.
Second, you're going to have to wrap that function in yet another function to ensure that it's given the correct value of i when it fires. The reason is effectively this: Your intention is to tell the function "when you're ready, use what i was when I set you up." However, what you're doing right now is effectively saying "when you're ready, look at i". Because the function doesn't check what i is until it's ready to fire, it won't know its value until long after you have performed the loop, meaning i will be a number much higher than you want!
As a bit of a sub-point to the above, you'll want to call that function immediately. This is known as an immediately invoked function expression. If you're not familiar with them, they're certainly worth looking up. Their uses are a bit unusual, but they're a powerful tool in the right spot.
Finally, because you're setting up everything right here and now, you want to make sure the timeout for each function is a second apart; as it stands now, you're saying "do all of these one second from now", when your intention is "do all of these one second apart, starting one second from now". This fix is relatively easy; all you need to do is multiply your timeout by i so that you set up the first to go 1 second from now, the second to go 2 seconds from now, and so on.
All of that combined gives you code that looks something like this:
loopThroughSplittedText: function(splittedText) {
for (var i = 0; i < splittedText.length; i++) {
setTimeout(
(function(locationInString) {
return function() {
console.log(splittedText[locationInString]);
};
}(i)),
(1000*i)
);
}
},
As for which solution is better, I would probably recommend the recursive function. The recursive version will only create one function that calls itself for every string you pass it, whereas the for loop version will create one function for every character in the string, which could get out of hand very quickly. Function creation (and object creation in general) can get expensive in JavaScript when you're working on larger projects, so it's generally best to favor solutions that avoid creating massive amounts of functions when possible.
But still, for sake of explanation, I wouldn't want to leave you without the for loop version; the knowledge could come in handy in other places. :)
A recursive function call would do the job:
var a = [
1,2,3,4,5,6,7,8,9,10
];
function log(i){
console.log(a[i]);
if (i<a.length){
setTimeout(function(){
i++;
log(i);
},1000);
}
}
log(0);
http://jsfiddle.net/Curt/rjve4whe/1/
In my example, it will show you how to loop through an array contentiously until you stop. This is to just give you an idea on how you can do the delay. Also it shows you when the value actually got displayed.
I would say that you could actually create a nice utility from this timer, and use it for multiple purposes and with the utility it'll stop you from repeating large chunks of code.
JavaScript Loop example:
var body = document.body;
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
loopThroughArray(splittedText, function (arrayElement, loopTime) {
body.innerHTML += arrayElement+ ": " + loopTime+ "<br/>";
}, 1000);
function loopThroughArray(array, callback, interval) {
var newLoopTimer = new LoopTimer(function (time) {
var element = array.shift();
callback(element, time - start);
array.push(element);
}, interval);
var start = newLoopTimer.start();
};
// Timer
function LoopTimer(render, interval) {
var timeout;
var lastTime;
this.start = startLoop;
this.stop = stopLoop;
// Start Loop
function startLoop() {
timeout = setTimeout(createLoop, 0);
lastTime = Date.now();
return lastTime;
}
// Stop Loop
function stopLoop() {
clearTimeout(timeout);
return lastTime;
}
// The actual loop
function createLoop() {
var thisTime = Date.now();
var loopTime = thisTime - lastTime;
var delay = Math.max(interval - loopTime, 0);
timeout = setTimeout(createLoop, delay);
lastTime = thisTime + delay;
render(thisTime);
}
}
Ok, as It is not an exact duplicate, you need to increate the delay in the loop, also escape from the closure variable in a loop issue
loopThroughSplittedText: function (splittedText) {
splittedText.forEach(function (text, i) {
setTimeout(function () {
console.log(text);
}, i * 1000)
})
}
var obj = {
loopThroughSplittedText: function(splittedText) {
splittedText.forEach(function(text, i) {
setTimeout(function() {
document.getElementById('x').innerHTML += text
}, i * 1000)
})
}
}
obj.loopThroughSplittedText('abcde'.split(''))
<div id="x"></div>
One problem with your code is that i is common to all the callbacks. So the first callback is told "output the entry at index i", however by the time it gets to execute the initial loop is finished so i is now at the end of the text.
One way to achieve what you're looking for is to not use a for loop, but to have a function which (1) prints a character, (2) updates the counter/position, and (3) schedules the next character if needed:
loopThroughSplitText: function (text) {
var i = 0;
function printEntry() {
console.log(text[i]);
i++; // Increment the position
if (i < text.length) { // If there are more chars, schedule another
setTimeout(printEntry, 1000);
}
}
printEntry(); // Print the first entry/char
}
solution using closure
https://jsfiddle.net/x3azn/pan2oc9y/4/
function loopThroughSplittedText(splittedText) {
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
for (var i = 0; i < splittedText.length; i++) {
// for each iteration console.log a word
// and make a pause after it
(function(_i) {
setTimeout(function() {
window.document.getElementById('text').innerHTML = splittedText[_i];
console.log(splittedText[_i]);
}, 1000)
}(i));
}
}
loopThroughSplittedText()
One more solution, with a setInterval:
var i = 0;
var intv = setInterval(function() {
if (i >= splittedText.length) {
clearInterval(intv);
} else {
console.log(splittedText[i]);
++i;
}
}, 1000);
There are a couple of problems here
setTimeout should take a function, not the result of calling a function
setTimeout returns immediately, so all the actions in your loop will be started at roughly the same moment, and all wait 1000ms before execting (notwithstanding the comment above however, which means they're all executed at the same moment).
The value of i will all be equal to splittedText.length for each iteration due to not wrapping your loop control variable in a closure.
What you need to do, is wait until the setTimeout instructions are executed before moving on to the next iteration of the loop.
For example:
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
function loopThroughSplittedText(splittedText) {
displayValue(splittedText,0);
}
function displayValue(arr, i){
if(i<arr.length){
setTimeout(function(){
document.getElementById('text').innerHTML = arr[i];
console.log(arr[i])
displayValue(arr,i+1);
},1000)
}
}
loopThroughSplittedText(splittedText)
Live example: http://jsfiddle.net/d3whkjww/1/
This will also work
function loopThroughSplittedText(splittedText) {
for (var i=0; i < splittedText.length;i++) {
(function(ind, text) {
setTimeout(function(){console.log(text);}, 1000 + (1000 * ind));
})(i, splittedText[i]);
}
}
Another sample:
var split = 'Lorem ipsum dolor'.split(' ');
var loop = function() {
console.log(split[0]);
split = split.slice(1);
if (split.length > 0) {
setTimeout(function() {
loop();
}, 1000);
}
}
loop();
Bringing out an alternative solution to the problem, which is making use of the third argument to setTimeout which is only supported in newer browsers:
(function (splittedText) {
for (var i = 0; i < splittedText.length; i++) {
setTimeout(
function(val) { console.log(val); },
i * 1000,
splittedText[i]
);
}
})(["Hello", "world", "!"]);
API documentation can be seen here (note the optional params).
You can achieve by 3 ways
1. closure
2. Recursive
3. variable declaration using let
var data = ['a', 'b', 'c', 'd'];
closure:
for(i=0; i<=data.length; i++) {
(function(x) {
setTimeout(() => {
console.log(x);
}, 1000)
})(data[i]);
}
let variable declaration
for(const ind of data) {
let local = ind;
setTimeout(() => {
console.log(local);
}, 1000)
}
This question already has answers here:
setTimeout in for-loop does not print consecutive values [duplicate]
(10 answers)
Closed 8 years ago.
i'm writing a code and i've stuck with setTimeout function. I would like to get animation effect. Display elements of array with delay. Code is done.
for (var i=0;i <= array.length-1;i++) {
(function(el) {
setTimeout(function(){
document.getElementById('Result').innerHTML += Math.floor(el);
console.log(Math.floor(el));
}, 3000*(i+1));
})(array[i]);
I had problem with delay when i use for (var i=array.length-1; i>=0;i--) Why? (The idea of this code is display items array form the last to the first)
for (var i=0;i <= array.length-1;i++) {
(function(el) {
setTimeout(function(){
Give now the same resultat as: for (var i=array.length-1; i>=0;i--) {
console.log(array[i]+'!')
The problem here is about closures. A closure is a type of anonymous function used to remember the arguments for using them later in asynchronous code (e.g. in functions called by setTimeout).
If you write something like:
setTimeout(function(foo){...}(value), 3000)
the function gets called before calling setTimeout. You have to use a closure to do this:
(function(foo){
setTimeout(function() {
... do something with foo ...
}, 3000);
})(value_of_foo);
This code will remember the value of foo creating a setTimeout with a function that uses it.
Another problem is that you have to increase the setTimeout time to create a delay, otherwise the for will create a bunch of setTimeout(..., 3000) that will be executed all at once. So what you will need to do to your code is something like the following:
for (var i = 0; i <= array.length; i++) {
(function(el) {
setTimeout(function(){
document.getElementById('Result').innerHTML += Math.floor(el)
}, 3000 * i);
})(array[i]);
}
Timeouts don't execute until after your main function has finished, this means that by the time your timeout function executes per loop, your i variable will be at its final value (in this case = 0). Your function declaration is also incorrect as the timeout function does not pass in those parameters for you. To do this, you need to wrap your timeout inside of another function call that takes in the parameters for the current loop, try something like this instead...
for (var i=array.length-1; i>=0;i--) {
function(array, i) {
setTimeout(function() {
document.getElementById('Result').innerHTML += Math.floor(array[i]);
}, (i+1) * 3000);
}(array, i);
}
First of all you are immediately calling the function and its result is assigned as the callback of the timeout. (this is wrong as the first argument must be a function)
Then, you have some syntax errors with missing closing } and )..
Try
for (var i=array.length-1; i>=0;i--) {
function(array,i){
setTimeout(function(){
document.getElementById('Result').innerHTML += Math.floor(array[i]);
}, 3000*i);
}(array,i);
}
I used 3000*i so that each timeout is 3 seconds after the other
This question already has answers here:
All the setTimeouts inside javascript for loop happen at once
(2 answers)
Closed 10 years ago.
I searched around and found some others with similar issues, but I can't seem to find a solution or clear explanation.
var content = 'test<br />';
for( var i = 1; i < 6; i++ ) {
setTimeout(function() {
document.write(content);
}, 3000);
}
I'd like the code in the for loop to execute 5 times, with a three second delay between each loop. When it runs it, at least on the surface, looks like a three second delay at page load, then goes through all the loops with no delay.
What am I missing?
Your problem is that all the calls are happening after 3000 ms. Do perform each call 3s apart do this:
var content = 'test<br />';
for( var i = 1; i < 6; i++ ) {
setTimeout(function() {
document.write(content);
}, 3000 * i);
}
You probably need to use setInterval ('cause you're trying to run code at a certain "interval")
// first create an isolated namespace because we don't need to dirty the global ns //
(function(){
var counter = 0;
var maxIterations = 6;
var intervalReference = setInterval(function(){
// your code goes here //
alert('test');
// the stop condition //
++counter;
if (counter == maxIterations) {
clearInterval(intervalReference);
}
}, 3000);
}())
setInterval is probably the way to go (see Alin's answer) but if you were wanting to go down the setTimeout route, the code would look something like this:
var loop = 0;
var content = "test<br>";
function startTimeout(init){
if(init!==true){
document.write(content);
loop++;
}
if(loop<5){
setTimeout(startTimeout, 3000);
}
}
startTimeout(true);