首页 > 解决方案 > for循环中的setTimeout

问题描述

我正在学习异步编程、闭包等。我有一些当前的困境。这是代码:

  for (var i = 1; i <= 3; i++){
  setTimeout(function(){
    alert(i + " second(s) elapsed");
  }, i * 3000);
}

我知道在调用 setTimeout 函数时, for 循环已经完成(使用 value i=4)。我的问题:

1. 的价值是i*3000什么?是:3000,6000,9000 还是 3000,3000,3000?

2.setTimeout函数在for循环中。如果在循环结束并关闭后调用它,它实际存储在哪里?

3.var i在for循环中声明。因此,当 for 循环完成并关闭时,它应该从范围中删除。那么 setTimeout 函数如何访问它的值呢?

标签: javascript

解决方案


1.i*3000的值是多少?是:3000,6000,9000 还是 3000,3000,3000?

您的警报将显示 4,4,4(i循环完成后的值)。

您的setTimeout()呼叫将获得 3000、6000、9000 的时间值。 setTimeout()是非阻塞的。所以,定时器被设置,然后for循环继续。

这里的问题是您alert()for循环完成后运行。因此,您将在循环i结束时提醒 的值。for如果您更改为使用let方式:

for (let i = 1; i <= 3; i++){
  setTimeout(function(){
    alert(i + " second(s) elapsed");
  }, i * 3000);
}

i然后,您可以单独访问每个循环值,alert()并且您alert()将显示 1、2、3。

另外,请切换到使用console.log(i + " second(s) elapsed");asalert()可能会弄乱事情的时间(这里没有,但可以)。

2.setTimeout函数在for循环中。如果在循环结束并关闭后调用它,它实际存储在哪里?

目前尚不清楚“实际存储”是什么意思。 setTimeout()是内置于 Javascript 引擎核心的系统函数。活动计时器存储在 JS 引擎内部。

3.var i 在 for 循环中声明。因此,当 for 循环完成并关闭时,它应该从范围中删除。那么 setTimeout 函数如何访问它的值呢?

声明的变量var的作用域是包含函数,而不仅仅是循环,因此它可以在包含函数的任何地方使用。

如果您使用let而不是var(您通常应该在任何现代 Javascript 引擎中养成习惯),那么它的作用域将只限于循环本身,实际上,每次调用都会有一个单独的变量实例循环,因此循环内的异步回调可以访问i属于其特定循环调用的。

垃圾回收注意事项

使用 时let,一旦循环完成,变量就会超出范围,但在循环内的所有代码不再可访问之前,它将不符合垃圾回收条件。因此,只要其中一个计时器仍然处于活动状态,访问i循环内 from 范围内值的代码仍然处于活动状态,因此每个i计时器都不能被垃圾收集,直到访问它的计时器触发。

重要的是要认识到垃圾收集不仅仅受范围控制。一个变量只有在没有可访问的代码访问它时才有资格进行垃圾回收。var因此,包含函数或块(取决于let变量被声明的位置)可能早就完成了,但是如果函数或块内部仍然可以调用的异步操作(如您的计时器回调)仍然有引用到变量,那么它仍然有一个正的引用计数并且还不能被垃圾收集。

请注意,这与在 C/C++ 等典型堆栈框架世界中考虑变量的生命周期非常不同。尽管 JS 引擎内部的实现可能比这更复杂,但我的简单模型是,我认为堆栈帧本身(使用函数或块声明的所有变量)本身会被垃圾收集,以便它一直存在,直到有函数中没有代码(包括异步回调)仍然可以到达它。

非阻塞注意事项setTimeout()

看来您可能对如何setTimeout()在循环中调用多个调用感到困惑,但循环会继续运行。这是因为setTimeout()是非阻塞的。这意味着它会在 JS 引擎内部注册一个计时器,然后立即返回,让您的for循环继续运行。然后,一段时间后,在for循环完成并设置所有三个计时器之后,JS 引擎调用与每个计时器关联的回调,并且该回调中的代码运行。

想想非阻塞概念,例如在日历中设置提醒。你设置了提醒(比如下午 4 点的约会),然后你继续做你的其他事情。然后,就在您下午 4 点的约会之前,日历会通知您即将到来的约会。提醒的设置是非阻塞的。提醒已注册,然后您可以继续进行其他业务,直到那时为止。这是同样的方式setTimeout()几乎所有的异步操作都在 Javascript 中工作。它们是非阻塞的。它们被启动或计划或启动,然后它们立即返回,允许您的 Javascript 继续做它想做的任何其他事情。然后,一段时间后,当异步操作完成并且 JS 解释器没有做其他事情时,与异步操作完成相关的回调被调用,并且该回调中的 JS 运行。


有关 Javascript 的事件驱动性质的更多信息,请参阅以下内容:

JavaScript 如何在后台处理 AJAX 响应?以及该帖子中的 9 个引用链接。

为什么while循环会阻塞事件循环?

setTimeout 在 Node.js 中等待时间过长

等到 flag=true


推荐阅读