如何在for循环中使用setInterval函数

34

我想在给定一个可变的项目列表时运行多个计时器。 代码大致如下:

var list = Array(...);

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

以上代码的问题在于只有列表末尾的项被更新,更新后的值是列表长度乘以末尾项的值。

能否提供一个解决方案并解释一下为什么会出现这种情况?

7个回答

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);
}

这是可用的代码:

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);
}

这里的索引i存储在匿名函数中,以便它不会被连续循环覆盖。您代码中的setInterval函数仅保留对i的最后一个值的引用。


优秀的解决方案!小提示:循环中可以更改的任何内容都可以传递给匿名函数,而不仅仅是索引。 - Muntashir Akon
非常感谢!使用匿名函数完美运行! - quinqui

43

所以,有几件事情:

  1. 最重要的是,你传递给setInterval()的回调函数保持对x的引用,而不是它在每次迭代期间存在的快照值。因此,当x在循环中被更改时,它也会在每个回调函数中更新。
  2. 此外,for...in用于枚举对象属性,并且在用于数组时可能表现出意外行为
  3. 更重要的是,我怀疑你真正想要setTimeout()而不是setInterval()

您可以通过向setTimout()提供其他参数来将参数传递给回调函数:

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

数字将按值传递而不是引用。以下是一个例子:

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
}


是的,我可以推荐阅读这篇文章:http://blog.morrisjohns.com/javascript_closures_for_dummies。但是除此之外,在循环内使用setTimeout可能并不是他想要做的事情,因为所有回调函数都会同时触发,它们不会被错开。 - SoWeLie
OP并不“需要一个闭包”,相反,它有一个对x的闭包需要避免(而您的答案实际上做到了这一点)。但它仍然保留了对list的闭包。 - RobG

6

您可以将forEachsetTimeout结合使用,以间隔循环遍历数组。

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)
})


4
您不必使用for循环来配合setInterval语句。试试以下代码:
var list = Array(...);
var x = 0;

setInterval(function() {

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

    else return;

    x++;
}, 5000);

2
我不知道如何使用for循环来做到这一点,但是下面的代码将在定时间隔内打印出数组中的每个元素:
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();

使用一些jQuery代码来在浏览器中展示它。

1
请看这个最简单的解决方案。它也适用于for循环。它看起来像一个间隔,但实际上是一个针对每次迭代增加的超时时间。对于每次迭代,setTimeout会翻倍。

for(let i=1; i<=10;i++){
    setTimeout(function(){
        console.log(i)
    },i*1000)
}


0
如果您有JSON数组和已包含jQuery,则可以使用以下代码:

$.each(jsonArray, function(i, obj) {
    setInterval( function() {
        console.log(i+' '+obj);
    }, 10);
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接