在 for 循环中使用 setTimeout 不会打印连续的值。

40 浏览
0 Comments

在 for 循环中使用 setTimeout 不会打印连续的值。

这个问题已经在这里有了答案:

JavaScript循环内部的闭包 - 简单实用的例子

我有这个脚本:

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

但是,两次都弹出 3,而不是先弹出 1,然后弹出 2

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

admin 更改状态以发布 2023年5月21日
0
0 Comments

你可以使用立即执行函数表达式(IIFE)在setTimeout周围创建一个闭包:

for (var i = 1; i <= 3; i++) {
    (function(index) {
        setTimeout(function() { alert(index); }, i * 1000);
    })(i);
}

0
0 Comments

你必须为每个超时函数准备一个独立的“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

0