setTimeout 在 forEach 中不起作用
Posted
技术标签:
【中文标题】setTimeout 在 forEach 中不起作用【英文标题】:setTimeout not working inside forEach 【发布时间】:2016-10-24 23:02:58 【问题描述】:我有一个调用函数的 forEach。每次调用之间需要有一个延迟。我把它放在了 forEach 中的 setTimeout 中。它不尊重第一次等待后的超时。相反,它等待一次,然后立即运行。我已将超时设置为 5 秒,并且正在使用控制台进行确认。等待 5 秒,然后同时记录多个 foobar 控制台。
为什么会出现这种情况?
var index = 0;
json.objects.forEach(function(obj)
setTimeout(function()
console.log('foobar');
self.insertDesignJsonObject(obj, index);
, 5000);
);
【问题讨论】:
为什么您认为对setTimeout
的每次调用都是连续的?提示:所有定时器同时运行。
如果你想要一个像first object ... 5secs ... second object ... 5 secs ... third object
这样的序列,你必须“松开”循环,即删除forEach,将counter设置为0,获取索引为“counter”的元素,开始一个超时,在超时的回调中设置计数器为1,获取索引为1的元素等
这可能是由于异步性质而发生的。
index
变量在哪里定义?
@Goose 问题在于简化的代码缺少解释实际问题所需的部分。有时你可以简化太多,所以问题变得很难正确回答。
【参考方案1】:
setTimeout 是异步的。它的作用是注册一个回调函数并将其置于后台延迟后触发。而 forEach 是同步函数。因此,您的代码所做的是“一次性”注册回调,每个回调将在 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,尽管它会根据时间间隔定期触发。在这里,我们访问对象并更新区间函数的索引inside。最后不要忘记清除间隔。
这两者之间的区别在于 setInterval 只注册了一个函数,而 setTimeout 则注册了列表中的项目数。
【讨论】:
【参考方案2】: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);
);
因为您可以访问“封闭式”index
变量 - 您可以依赖它的状态是调用函数 (lambda) 时的预期状态!
其他资源:
How do JavaScript closures work?
http://javascript.info/tutorial/closures
http://code.tutsplus.com/tutorials/closures-front-to-back--net-24869
【讨论】:
实际上,我认为细读的新手会期望您的第一段代码打印0 (no time passes) 1 (1 second passes) 2 (2 seconds pass)...
,因为您将1 * 1000
作为时间部分:)。
很好的答案!但这里有一个错误。 What you would actually see is the number 10 getting logged 10 times all-at-once!
不是一次性的,它会是每秒一个日志。
@MikeMcCaughan 啊!好点子,改正了。我 +1 你的评论 :)
@JasonXie 也很棒。固定。以上是关于setTimeout 在 forEach 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章
SetTimeout 在 Mongoose 模式后中间件中不起作用
JavaScript 调用`setTimeout` 在 Oracle Apex 中不起作用
Javascript函数显示带有setTimeout的隐藏按钮在PDF中不起作用
Puppeteer 等待页面和 setTimeout 在 Firebase 云功能中不起作用