我有一个调用函数的forEach。每次调用之间都需要有延迟。我把它放在forEach里面的setTimeout里面。第一次等待后不遵守超时。相反,它等待一次,然后立即运行。我将超时设置为5秒,我正在使用控制台进行确认。 5秒等待,然后几个foobar控制台同时记录。
为什么我会出现这种行为?
var index = 0;
json.objects.forEach(function(obj) {
setTimeout(function(){
console.log('foobar');
self.insertDesignJsonObject(obj, index);
}, 5000);
});
答案 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相比,注册了列表中的项目数。