This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 1 year ago.
Hello i want to have 4 timeOuts to update balls positions because the balls don't go at the same speed.
here's a part of my code:
var i=1;
while(i<=5)
{
console.log(i);
intervals[i-1]=setInterval(function()
{
MoveBallHorizontalyWithSpeed(i);
MoveBallVerticalyWithSpeed(i);
showBalls();
console.log(i);
},speed/i);
i++;
}
the problem is that each timeout calls the functions with 6 but i whant the first timeout calling MoveBallHorizontalyWithSpeed(1) the second MoveBallHorizontalyWithSpeed(2) etc...
is there a way to do that faster than writing each timeout?
The problem is that you are using a global variable inside the loop so all the setInterval functions are using the same variable i, in this type of scenario always use a local variable. You should change your code like this:
for(let i = 1; i<=5; i++)
{
console.log(i);
setInterval(function()
{
console.log(i);
},2000);
}
Please try this:
var i=1;
while(i<=5)
{
console.log(i);
const j = i;
intervals[i-1]=setInterval(function()
{
MoveBallHorizontalyWithSpeed(j);
MoveBallVerticalyWithSpeed(j);
showBalls();
console.log(j);
},speed/j);
i++;
}
The reason the while loop will finished its execution and will update the value of the i. So when setInterval execute it get the most latest value of i that is 6.
To avoid this you can create a closure and an IIFE and pass the value of i as a parameter to IIFE. Inside IIFE you can call setInterval
var i = 1;
intervals = [];
while (i <= 5) {
intervals[i - 1] = (function(x) {
setInterval(function() {
MoveBallHorizontalyWithSpeed(x);
MoveBallVerticalyWithSpeed(x);
}, 10 / x);
})(i)
i++;
}
function MoveBallVerticalyWithSpeed(speed) {
console.log(speed)
}
function MoveBallHorizontalyWithSpeed(speed) {
console.log(speed)
}
Related
I need some explanation to understand clearly what is happening here ;
We have these two code samples , the first one logs to the console -1 five times ,and that is because the for loop executes completely leaving i with the value –1, and only then do the callbacks start executing. The problem
is, when they execute, i already has the value –1.
The second sample logs the expected result which is a 5 to 1 countdown.The only difference between the two samples is that i is no longer declared in the scope of the countdown() function , but why does that change the execution and how is the value of i being handled in the second sample?
Code Sample #1
function countdown() {
let i; // note we declare let outside of the for loop
console.log("Countdown:");
for(i=5; i>=0; i--) {
setTimeout(function() {
console.log(i===0 ? "GO!" : i);
}, (5-i)*1000);
}
}
countdown();
Code Sample #2
function countdown() {
console.log("Countdown:");
for(let i=5; i>=0; i--) { // i is now block-scoped
setTimeout(function() {
console.log(i===0 ? "GO!" : i);
}, (5-i)*1000);
}
}
countdown();
In sample 1, the variable is declared just inside the function. You get a new i every time countdown() is called. Within countdown() the variable changes. By the time the timeout runs, i will be on its lowest value.
In sample 2, the variable is declared just inside the loop. You get a new i every time the loop goes around. This means you get a new i for every function you pass to setTimeout. The different timeouts are no longer sharing the same variable.
The key difference is the use of the let keyword within the for loop.
In code sample #1, i is declared outside of the for loop. That means each access to i in each closure causes the same i to be dereferenced, because i is within the identical block scope of each closure, which is why we get -1 each time.
In code sample #2, i is declared within the for loop, which means a new i is created for each execution. Each closure refers its own, exclusive block in the for loop which has a unique i. Thus, we get the expected 5 to 1 countdown.
For use of ES6 features like let, it can be handy to use the Babel REPL to determine how scoping rules are applied. The following simplified versions of code blocks #1 and #2:
for (var j = 0; j < 5; ++j) {
setTimeout(function() { console.log(j); }, 1);
}
for (let i = 0; i < 5; ++i) {
setTimeout(function() { console.log(i); }, 1);
}
...are compiled into:
"use strict";
for (var j = 0; j < 5; ++j) {
setTimeout(function () {
console.log(j);
}, 1);
}
var _loop = function _loop(i) {
setTimeout(function () {
console.log(i);
}, 1);
};
for (var i = 0; i < 5; ++i) {
_loop(i);
}
Notice how, for code block #2, i gets copied into a distinct function scope using the _loop function where each closure refers to its own i.
Check it out here.
This question already has answers here:
How do I add a delay in a JavaScript loop?
(32 answers)
Closed 8 years ago.
I have a function set up like this
awesomeFirePenguin: function(){
// function does some stuff, not important
},
and I call it in a for loop like this
for (var i = 0; i < n; i++){
this.awesomeFirePenguin();
}
This works, the function is called n times, but the results of the function are shown immediately after each other. What I want is for the results to be shown after each other with a slight delay, like 100ms or so. Something klike this
for (var i = 0; i < n; i++){
this.awesomeFirePenguin();
// wait a while and then continue
}
I tried using
for (var i = 0; i < n; i++){
window.setTimeout(this.awesomeFirePenguin(), 100);
}
and window.setInterval(this.awesomeFirePenguin(), 100);, but both only execute the function once.
setTimeout and setInterval can take functions as arguments, but what you were doing is calling them. Thus, they were receiving not the function, but its return value as an argument.
If you want to delays to stack, you can just multiple the timeout delay by whatever loop index you're currently at.
for (var i = 0; i < n; i++){
window.setTimeout(this.awesomeFirePenguin.bind(this), 100 * i);
}
The answer by voithos is probably the simplest. You can also use an asynchronous execution library like Clumpy.js, where you could write your code as:
var clumpy = new Clumpy({delay: 100});
var i;
clumpy.for_loop(
function() { i = 0; },
function() { return i < n; },
function() { ++i; },
this.awesomeFirePenguin
);
It's also possible to do this using a closure and a self-invoking function, but it's ugly code that's hard to understand:
(function loop(i, f) {
setTimeout(function () {
f();
if (--i) loop(i, f);
}, 100)
}(n, this.awesomeFirePenguin));
It's possible to write something using setInterval instead of setTimeout, but it's not any easier to comprehend, although it probably eliminates the recursive calls.
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:
Closed 12 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
Rather than explaining the question, I'll give an example:
for (var i = 0; i < 100; i ++) {
get_node(i).onclick = function() {
do_something_very_important(i);
}
}
Is there any way to have the value of i substituted into the function upon creation rather than execution? Thanks.
Yes, you can, but that won't work for the example you provided. You would be having a very common closure problem in that for loop.
Variables enclosed in a closure share the same single environment, so by the time the onclick callback is called, the for loop will have run its course, and the i variable will be left pointing to the last value it was assigned. In your example, the do_something_very_important() function will be passed the value 100 for each node, which is not what you intend.
You can solve this problem with even more closures, using a function factory:
function makeClickHandler(i) {
return function() {
do_something_very_important(i);
};
}
// ...
for(var i = 0; i < 100; i++) {
get_node(i).onclick = makeClickHandler(i);
}
This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following Mozilla article for a brief introduction:
Mozilla Dev Center: Working with Closures
UPDATE:
You could also inline the above function factory as #adamse suggested in the other answer. This is actually a more common approach, but is practically the same as the above:
for(var i = 0; i < 100; i++) {
get_node(i).onclick = (function(p) {
return function () {
// we could have used i as a parameter variable as well,
// but we're using p to better illustrate what's happening
do_something_very_important(p);
}
})(i);
}
Any yet another solution is to enclose each iteration in its own scope, by using self invoking anonymous functions:
for(var i = 0; i < 100; i++) {
(function (p) {
// we now have a separate closure environment for each
// iteration of the loop
get_node(i).onclick = function() {
do_something_very_important(p);
}
})(i);
}
Yes this works...
for (var i = 0; i < 100; i++) {
get_node(i).onclick = (function(i) {
return function () {
do_something_very_important(i);
}
})(i);
}
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I'm having a problem with some JavaScript code.
Script
setTimeout(function() {
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 200);
}
}, 200);
Outputs
5, 5, 5, 5, 5 instead of 1, 2, 3, 4, 5
I can kind of understand why this doesn't work, but I was wondering if someone could explain to me what's happening and why it's not working!
Also, how can this scope problem be overcome?
The setTimeout callback functions are executed asynchronously, all the console.log calls you make refer to the same i variable, and at the time they are executed, the for loop has ended and i contains 4.
You could wrap your inner setTimeout call inside a function accepting a parameter in order to store a reference to all the i values that are being iterated, something like this:
setTimeout(function() {
for (var i = 0; i < 5; i++) {
(function (j) { // added a closure to store a reference to 'i' values
setTimeout(function() {
console.log(j);
}, j * 200);
})(i); // automatically call the function and pass the value
}
}, 200);
Check my answer to the following question for more details:
Variables in Anonymous Functions — Can someone explain the following?
Take a look at this question. It might help you understand the scope and closures better, very similar to your question.
You're trying to create a closure containing the variable "i". But closures are only created at the end of a function. So if your functions are created in a for loop, they will all have the values from the last iteration.
You can fix it with something like this:
var createFunction = function(index) {
return function() {
console.log(index);
}
};
for (var i = 0; i < 5; i++) {
setTimeout(createFunction(i), i * 200);
}
where you return the function from another function.
The variable i exists in the scope of the outer function.
It changes as the loop runs.
The inner function references it.
Try something like this:
var i_print_factory = function (value) {
return function () {
console.log(value);
};
};
var init_timers = function () {
for (var i = 0; i < 5; i++) {
setTimeout(i_print_factory(i), i * 200);
}
};
setTimeout(init_timers, 200);
Because you are accessing the same variable i in all the functions used in set timeout. The setTimeout function sets the function to fire the number of milliseconds in the future on the same thread as the i variable. The i value isn't copied in the function, the function is referencing the actual variable i when it is fired. Because you have looped through parent function until the i = 5 and this is done before anything else has a chance to fire, they all show up as 5.