How to use setInterval function within for loop
Asked Answered
C

7

34

I'm trying to run multiple timers given a variable list of items. The code looks something like this:

var list = Array(...);

for(var x in list){
    setInterval(function(){
        list[x] += 10;
        console.log(x + "=>" + list[x] + "\n");
    }, 5 * 1000);
}

The problem with the above code is that the only value being updated is the item at the end of the list, multiplied by the number of items in the list.

Can anyone offer a solution and some explanation so I know why it's behaving this way?

Chairmanship answered 13/10, 2011 at 3:19 Comment(0)
N
43

So, a few things:

  1. Most importantly, the callback function you've passed to setInterval() maintains a reference to x rather than the snapshot value of x as it existed during each particular iteration. So, as x is changed in the loop, it's updated within each of the callback functions as well.
  2. Additionally, for...in is used to enumerate object properties and can behave unexpectedly when used on arrays.
  3. What's more, I suspect you really want setTimeout() rather than setInterval().

You can pass arguments to your callback function by supplying additional arguments to setTimout():

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

Numbers will be passed by value rather than reference. Here's an example:

var list = [1,2,3,4];

for (var x = 0, ln = list.length; x < ln; x++) {
  setTimeout(function(y) {    
    console.log("%d => %d", y, list[y] += 10);
  }, x * 500, x); // we're passing x
}
Nolan answered 13/10, 2011 at 3:21 Comment(2)
Yes, might I recommend reading this article: blog.morrisjohns.com/javascript_closures_for_dummies. On top of that though, setTimeout within a loop may not be what he wants to do anyway as all of the callbacks will be fired at the same time, they will not be staggered.Sherborne
The OP doesn't "need a closure", quite the opposite—it has a closure to x that needs to be avoided (which your answer actually does). It keeps the closure to list though.Guardafui
C
45

var list = [1, 2, 3, 4, 5];

for (var i = 0, len = list.length; i < len; i += 1) {
    (function(i) {
        setInterval(function() {
            list[i] += 10;
            console.log(i + "=>" + list[i] + "\n");
        }, 5000)
    })(i);
}

Here is the working code:

var list = [1, 2, 3, 4, 5];

for (var i = 0, len = list.length; i < len; i += 1) {
    (function(i) {
        setInterval(function() {
            list[i] += 10;
            console.log(i + "=>" + list[i] + "\n");
        }, 5000)
    })(i);
}

Here the index i is stored in an anonymous function, so that it is not overwritten by consecutive loops. setInterval function in your code keeps the reference only to the last value of i.

Chaw answered 13/10, 2011 at 3:24 Comment(2)
Excellent solution! A small note: anything that can be changed in the loop can be passed to the anonymous function, not just index.Jungle
Thank you very much! Using the anonymus function works perfect!Jay
N
43

So, a few things:

  1. Most importantly, the callback function you've passed to setInterval() maintains a reference to x rather than the snapshot value of x as it existed during each particular iteration. So, as x is changed in the loop, it's updated within each of the callback functions as well.
  2. Additionally, for...in is used to enumerate object properties and can behave unexpectedly when used on arrays.
  3. What's more, I suspect you really want setTimeout() rather than setInterval().

You can pass arguments to your callback function by supplying additional arguments to setTimout():

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

Numbers will be passed by value rather than reference. Here's an example:

var list = [1,2,3,4];

for (var x = 0, ln = list.length; x < ln; x++) {
  setTimeout(function(y) {    
    console.log("%d => %d", y, list[y] += 10);
  }, x * 500, x); // we're passing x
}
Nolan answered 13/10, 2011 at 3:21 Comment(2)
Yes, might I recommend reading this article: blog.morrisjohns.com/javascript_closures_for_dummies. On top of that though, setTimeout within a loop may not be what he wants to do anyway as all of the callbacks will be fired at the same time, they will not be staggered.Sherborne
The OP doesn't "need a closure", quite the opposite—it has a closure to x that needs to be avoided (which your answer actually does). It keeps the closure to list though.Guardafui
U
6

You can combine forEach and setTimeout to loop over the array with the interval.

let modes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let interval = 1000; //one second
modes.forEach((mode, index) => {

  setTimeout(() => {
    console.log(mode)
  }, index * interval)
})
Uveitis answered 26/10, 2018 at 12:5 Comment(0)
D
5

You don't have to use a for cycle with the setInterval statement. Try this:

var list = Array(...);
var x = 0;

setInterval(function() {

    if (x < list.length;) {
        list[x] += 10;
        console.log(x+"=>"+list[x]);
    }

    else return;

    x++;
}, 5000);
Damp answered 12/9, 2013 at 10:30 Comment(0)
C
2

I don't know how to do this with a for loop but this code here will print out each element in an array at timed intervals:

function displayText(str) {
   $('.demo').append($('<div>').text(str));
}
var i = 0;

var a = [12, 3, 45, 6, 7, 10];

function timedLoop() {
setTimeout(function () {
    displayText(a[i]);
    i++;
    if(i < a.length) {
        timedLoop();
    }
}, 2000)
}

timedLoop();

Using a bit of jquery to show it in the browser.

Checkpoint answered 29/3, 2016 at 15:41 Comment(0)
B
1

Please look at this simplest solution. It does work with forloop too. It looks like an interval but it is a timeout increased for each iteration. For each iteration setTimeout doubles.

for(let i=1; i<=10;i++){
    setTimeout(function(){
        console.log(i)
    },i*1000)
}
Babettebabeuf answered 13/10, 2022 at 4:4 Comment(0)
K
0

If you have JSON array and jQuery included, you can use:

$.each(jsonArray, function(i, obj) {
    setInterval( function() {
        console.log(i+' '+obj);
    }, 10);
});
Killerdiller answered 4/12, 2014 at 10:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.