javascript - 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 函数如何访问它的值呢?
解决方案
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 个引用链接。
推荐阅读
- html - 在 flex-element 上定位 :target::before 不起作用
- c - C语言:删除向量的最后一个元素
- azure - 带有 FCM 的 Azure 通知集线器 - NullPointerException
- python-3.x - networkx中最短路径的降序
- vue.js - 计算变量不调用 set() 方法。在 v-for 中使用 v-model 计算变量
- laravel - Laravel 中间件分配多个角色不起作用
- sql-server-2017 - 无法在本地连接到 SQL Server 2017
- java - JUnit - @Before @After - 实例方法只被调用一次?如何?
- r - 为无序重复 dyads 分配平均值和/或条件分配
- java - Bug in recursive selection sort?