小编典典

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

all

我有这个脚本:

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

但是3两次都被提醒,而不是1then 2

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


阅读 66

收藏
2022-04-18

共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循环的每次迭代都有一个不同的。

2022-04-18