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 云功能中不起作用

如何调查为什么setTimeout函数在我的代码中不起作用?

数字格式在 Smarty 的 foreach 中不起作用