setTimeout在forEach中没有工作

时间:2016-06-22 20:23:42

标签: javascript function foreach settimeout

我有一个调用函数的forEach。每次调用之间都需要有延迟。我把它放在forEach里面的setTimeout里面。第一次等待后不遵守超时。相反,它等待一次,然后立即运行。我将超时设置为5秒,我正在使用控制台进行确认。 5秒等待,然后几个foobar控制台同时记录。

为什么我会出现这种行为?

var index = 0;
json.objects.forEach(function(obj) {
    setTimeout(function(){
        console.log('foobar');
        self.insertDesignJsonObject(obj, index);
    }, 5000);
});

2 个答案:

答案 0 :(得分:25)

Jason所说的答案完全正确,但我想我会试一试,以便更好地澄清。

这实际上是一个经典的闭包问题。通常它看起来像:

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

新手希望控制台显示:

0
(0 seconds pass...)
1
(1 second passes...)
2
(etc..)

但事实并非如此!您实际看到的是10被记录10次(每秒1次)!

“为什么会这样?”好问题。关闭范围。上面的for循环缺少闭包范围,因为在javascript中,只有函数(lambdas)具有闭包范围!

请参阅:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

然而!如果你尝试过这个尝试,你的尝试就会达到预期的输出:

    json.objects.forEach(function(obj,index,collection) {
        setTimeout(function(){
            console.log('foobar');
            self.insertDesignJsonObject(obj, index);
        }, index * 5000);
    });

因为您可以访问“closur-ed”index变量 - 在调用函数(lambda)时,您可以依赖其状态为预期状态!

其他资源:

How do JavaScript closures work?

http://javascript.info/tutorial/closures

http://code.tutsplus.com/tutorials/closures-front-to-back--net-24869

答案 1 :(得分:14)

setTimeout是异步。它的作用是注册一个回调函数并将其置于后台,这将在延迟后触发。而forEach是同步功能。所以你的代码所做的就是注册回调&#34;所有这些都是#34;每个将在5秒后触发。

避免它的两种方法:

设置索引以设置计时器。

json.objects.forEach(function(obj, index) {
    setTimeout(function(){
      // do whatever
    }, 5000 * (index + 1));
});

这种延迟因子是基于对象的索引,所以即使你同时注册它们,它也会根据自己的延迟触发。 index + 1保持相同的结果,因为它从0开始。

setInterval循环对象

var i = 0;
var interval = setInterval(function(){
    var obj = json.objects[i];
    // do whatever
    i++;
    if(i === json.objects.length) clearInterval(interval);
}, 5000);

setInterval与setTimeout类似,但它会根据间隔定期触发。在这里,我们访问对象并更新区间函数的索引里面。也不要忘记最后清除间隔。

这两者之间的区别是setInterval只注册了一个函数,与setTimeout相比,注册了列表中的项目数。