在for循环中使用setTimeout

时间:2018-10-12 18:05:32

标签: javascript

我正在学习异步编程,闭包等。我现在有一些难题。 这是代码:

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

我了解到调用setTimeout函数时,for循环已经完成(值i=4)。我的问题:

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

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

3。var i在for循环中声明。因此,当for循环完成并关闭时,应将其从作用域中删除。那么setTimeout函数如何才能访问其值?

1 个答案:

答案 0 :(得分:3)

  

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");,因为alert()可能会影响事情的时机(不是在这里,但可以)。

  

2.setTimeout函数位于for循环中。如果在循环结束并关闭后调用它,它的实际存储位置是什么?

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

  

3.var i在for循环中声明。因此,当for循环完成并关闭时,应将其从作用域中删除。那么setTimeout函数如何才能访问其值?

var声明的变量的作用域为包含函数,而不仅限于循环,因此它可以在包含函数中的任何位置使用。

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

有关垃圾回收的注意事项

使用let时,循环完成后,变量将超出范围,但直到不再能访问循环内的所有代码后,该变量才有资格进行垃圾回收。因此,只要其中一个计时器仍处于活动状态,从循环内部访问i的范围内值的代码仍将处于活动状态,因此,直到计时器(访问它。

重要的是要认识到,垃圾回收不仅受作用域控制。仅当没有可访问的代码访问该变量时,该变量才有资格进行垃圾回收。因此,包含函数或块(取决于声明变量的ivar)可能已经完成很长时间了,但是如果函数或块中还有异步操作仍可以调用( (例如您的计时器回调)仍具有对该变量的引用,则它的引用计数仍为正,并且无法进行垃圾回收。

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

关于非阻塞let

的说明

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

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


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

How does JavaScript handle AJAX responses in the background?和该帖子中引用的9个链接。

Why does a while loop block the event loop?

setTimeout waits too long in Node.js

Wait until flag=true