一尘不染

JavaScriptfor循环中的setTimeout不打印连续值

javascript

我有这个脚本:

for (var i = 1; i <= 2; i++) {
    setTimeout(function() { alert(i) }, 100);
}

3被警告两次,而不是1那么2

有没有一种方法可以传递i而不将函数编写为字符串?


阅读 379

收藏
2020-04-23

共1个答案

一尘不染

您必须为每个超时功能安排一个单独的“ i”副本。

function doSetTimeout(i) {
  setTimeout(function() { alert(i); }, 100);
}

for (var i = 1; i <= 2; ++i)
  doSetTimeout(i);

如果您不做这样的事情(同一想法还有其他变体),那么每个计时器处理程序函数将 共享 相同的变量“ i”。循环结束后,“
i”的值是多少?3!通过使用中间函数,将 复制 变量的值。由于超时处理程序是在该副本的上下文中创建的,因此它具有自己的专用“ i”。

编辑 -随着时间的流逝,出现了一些评论,由于设置了一些超时会导致处理程序同时执行所有操作,因此有些混乱显而易见。重要的是要了解, 设置
计时器的过程-
调用setTimeout()-几乎不需要时间。也就是说,告诉系统“请在1000毫秒后调用此函数”将几乎立即返回,因为在计时器队列中安装超时请求的过程非常快。

因此,如果进行了 一系列
超时请求(如OP中的代码和我的答案中的情况),并且每个时间延迟值都相同,那么一旦经过该时间量,所有计时器处理程序会迅速相继被称为。

如果您需要按间隔调用处理程序,则可以使用setInterval(),它的调用方式与完全相同,setTimeout()但是在重复请求数量的延迟后将触发多次,或者您可以建立超时并乘以时间您的迭代计数器的价值。也就是说,修改我的示例代码:

function doScaledTimeout(i) {
  setTimeout(function() {
    alert(i);
  }, i * 5000);
}

(在100毫秒级的时间内,效果不会很明显,因此我将该数字提高到5000。)将的值i乘以基本延迟值,因此在循环中调用5次将导致5次延迟。秒,10秒,15秒,20秒和25秒。

更新资料

在2018年,这里有一个更简单的选择。由于具有在比函数更狭窄的范围内声明变量的新功能,因此,如果修改了原始代码,则可以工作:

for (let i = 1; i <= 2; i++) {
    setTimeout(function() { alert(i) }, 100);
}

let声明不同var,声明本身会导致i循环的每次迭代都具有不同的内容。

2020-04-23