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

2022-08-29 23:58:08

我有这个脚本:

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

但是两次都发出警报,而不是然后。312

有没有办法传递,而不把函数写成字符串?i


答案 1

您必须为每个超时函数安排一个不同的“i”副本。

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

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

如果您不执行此类操作(并且同一想法还有其他变体),则每个计时器处理程序函数将共享相同的变量“i”。循环完成后,“i”的值是多少?是3!通过使用中介函数,可以创建变量值的副本。由于超时处理程序是在该副本的上下文中创建的,因此它有自己的私有“i”可供使用。

编辑 - 随着时间的推移,有一些评论,其中一些混淆是显而易见的,因为设置几个超时会导致处理程序同时触发。重要的是要了解,设置计时器的过程 - 调用 - 几乎不需要任何时间。也就是说,告诉系统,“请在1000毫秒后调用此函数”将几乎立即返回,因为在计时器队列中安装超时请求的过程非常快。setTimeout()

因此,如果发出了一连串的超时请求,就像OP和我的回答中的代码中的情况一样,并且每个请求的时间延迟值都是相同的,那么一旦经过该时间量,所有计时器处理程序将被快速连续地调用。

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

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

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

更新

在2018年,有一个更简单的选择。有了在比函数更窄的范围内声明变量的新功能,如果修改,原始代码将起作用:

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

与 不同,声明本身将导致循环的每次迭代都有一个不同的声明。letvari


答案 2

您可以使用立即调用的函数表达式 (IIFE) 在 以下周围创建闭包:setTimeout

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