简体   繁体   English

在for循环中使用setTimeout

[英]setTimeout in a for loop

I am learning about asynchronous programming, closures etc. I have a few current dillemas. 我正在学习异步编程,闭包等。我现在有一些难题。 Here`s the code: 这是代码:

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

I understand that by the time setTimeout functions are called, that the for loop is already done (with value i=4 ). 我知道在调用setTimeout函数时,for循环已经完成(值i=4 )。 My questions: 我的问题:

1.What are the values of i*3000 ? 1. i*3000的值是多少? Is it :3000,6000,9000 OR 3000,3000,3000? 是:3000,6000,9000还是3000,3000,3000?

2.setTimeout function is in the for loop.If it is called after the loop has finished and closed where is it actually stored? 2.setTimeout函数在for循环中,如果在循环结束并关闭后调用它,它实际存储在哪里?

3. var i is declared in the for loop. 3. var i在for循环中声明。 So, when for loop is finished and closed it should be deleted from the scope. 因此,当for循环完成并关闭时,应将其从作用域中删除。 How can setTimeout functions have access to its value then? setTimeout函数如何才能访问其值?

1.What are the values of i*3000? 1. i * 3000的值是多少? Is it :3000,6000,9000 OR 3000,3000,3000? 是:3000,6000,9000还是3000,3000,3000?

Your alerts will show 4,4,4 (the value of i after the loop is done). 您的警报将显示4,4,4(循环完成后i的值)。

Your setTimeout() calls will get fed a time value of 3000, 6000, 9000. setTimeout() is non-blocking. 您的setTimeout()调用将获得3000、6000、9000的时间值setTimeout()是非阻塞的。 So, the timer is set and then the for loop continues. 因此,设置了计时器,然后for循环继续。

The problem here is that your alert() runs AFTER the for loop is done. 这里的问题是for循环完成后,您的alert()运行。 So, you will be alerting the value of i at the end of the for loop. 因此,您将在for循环结束时提醒i的值。 If you change to use let as in: 如果更改为使用let as:

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

Then, each loop value of i will be separately accessible by your alert() and your alert() would show 1, 2, 3. 然后,您的alert()将分别访问i每个循环值,并且alert()将显示1、2、3。

Also, please switch to using console.log(i + " second(s) elapsed"); 另外,请切换到使用console.log(i + " second(s) elapsed"); as alert() can mess with timing of things (it doesn't here, but it can). 因为alert()可能会使事情发生时间混乱(不是在这里,但是可以)。

2.setTimeout function is in the for loop.If it is called after the loop has finished and closed where is it actually stored? 2.setTimeout函数在for循环中,如果在循环结束并关闭后调用它,它实际存储在哪里?

It's not clear what you mean by "actually stored". 不清楚“实际存储”是什么意思。 setTimeout() is a system function built into the core of the Javascript engine. setTimeout()是内置在Javascript引擎核心中的系统功能。 Active timers are stored inside of the JS engine. 活动计时器存储在JS引擎内部。

3.var i is declared in the for loop. 3.var i在for循环中声明。 So, when for loop is finished and closed it should be deleted from the scope. 因此,当for循环完成并关闭时,应将其从作用域中删除。 How can setTimeout functions have access to its value then? setTimeout函数如何才能访问其值?

Variables declared with var are scoped to the containing function , not just to the loop so it is available for use anywhere in the containing function. var声明的变量的作用域为包含函数 ,而不仅限于循环,因此可以在包含函数中的任何位置使用。

If you use let instead of var (which you should generally get in the habit of in any modern Javascript engine), then that would be scoped only to the loop itself and, in fact, there would be a separate instance of the variable for each invocation of the loop so an async callback inside the loop would have access to the i that belongs to its specific invocation of the loop. 如果使用let而不是var (通常在任何现代Javascript引擎中都应习惯使用var ),则该范围仅适用于循环本身,实际上,每个变量都有一个单独的变量实例循环的调用,因此循环内的异步回调将有权访问属于其特定循环调用的i

Notes on Garbage Collection 垃圾收集注意事项

When using let , the variable would be out of scope as soon as the loop was done, but it won't be eligible for garbage collection until all the code inside the loop is no longer reachable. 使用let ,变量在循环完成后将不在范围内,但是直到不再能访问循环内的所有代码后,该变量才有资格进行垃圾回收。 So, as long as one of the timers is still active, code that accesses the in-scope value of i from within the loop is still active and thus each i cannot be garbage collected until the timer that accesses it fires. 因此,只要其中一个计时器仍处于活动状态,那么从循环内访问i的作用域内值的代码仍将处于活动状态,因此,直到访问它的计时器触发之前,每个i都不会被垃圾回收。

It's important to realize that garbage collection is not controlled only be scope. 重要的是要认识到,垃圾回收不仅受范围控制。 A variable is eligible for garbage collection only when there is no reachable code that accesses it. 仅当没有可访问的代码访问该变量时,该变量才有资格进行垃圾回收。 Thus, the containing function or block (depending upon var or let where the variable was declared) may have long since completed, but if there are asynchronous operations inside the function or block that can still be called (like your timer callbacks) that still have a reference to the variable, then it still has a positive ref count and cannot yet be garbage collected. 因此,包含函数或块(取决于varlet声明变量的位置)可能已经完成很长时间了,但是如果函数或块内部仍可以调用(例如您的计时器回调)异步操作,对该变量的引用,则其引用计数仍为正,并且无法进行垃圾回收。

Note, this is very different than thinking about the lifetime of a variable in a typical stack frame world like C/C++. 请注意,这与思考变量在诸如C / C ++这样的典型堆栈框架世界中的生命周期完全不同。 Though the internal implementation inside the JS engine might be more involved than this, my simple model is that I think about the stack frame itself (all the variable declared with the function or block) being garbage collected itself so that it stays around until there is no code within the function (including asynchronous callbacks) that can still reach it. 尽管JS引擎内部的实现可能比这更复杂,但我的简单模型是我认为堆栈框架本身(使用函数或块声明的所有变量)本身都是垃圾收集的,因此可以一直存在直到函数内没有代码(包括异步回调)仍然可以到达它。

Notes on non-blocking setTimeout() 关于非阻塞setTimeout()注释

It appears you may be confused about how multiple setTimeout() calls can be called within the loop, but the loop keeps running. 您似乎对如何在循环中调用多个setTimeout()感到困惑,但是循环一直在运行。 This is because setTimeout() is non-blocking. 这是因为setTimeout()是非阻塞的。 That means it registers a timer internal to the JS engine and then immediately returns allowing your for loop to continue to run. 这意味着它会在JS引擎内部注册一个计时器,然后立即返回以允许您的for循环继续运行。 Then, some time later, after the for loop is done and all three timers have been set, the JS engine calls the callback associated with each timer and the code inside that callback runs. 然后,一段时间后,在完成for循环并设置了所有三个计时器之后,JS引擎将调用与每个计时器关联的回调,然后运行该回调中的代码。

Think of the non-blocking concept like setting a reminder in your calendar. 将非阻塞概念想像为在日历中设置提醒。 You set the reminder (say for a 4pm appointment) and then you keep going about your other business. 您设置提醒(比如说下午4点约会),然后继续进行其他业务。 Then, right before your 4pm appointment, the calendar notifies you of the upcoming appointment. 然后,在您下午4点约会之前,日历会通知您即将到来的约会。 The setting of the reminder was non-blocking. 提醒的设置是非阻塞的。 The reminder was registered and then you could keep going about your other business until then. 提醒已注册,然后您可以继续处理其他事务。 That's the same way setTimeout() works and pretty much all asynchronous operations in Javascript. 这与setTimeout()工作方式以及Javascript中几乎所有异步操作的方式相同。 They are non-blocking. 它们是非阻塞的。 They get initiated or scheduled or started and then they immediately return allowing your Javascript to keep doing whatever else it wanted to do. 他们开始,计划或启动,然后立即返回,允许您的Javascript继续执行其想做的任何事情。 Then, sometime later, when the asynchronous operation completes and the JS interpreter is not otherwise doing something, the callback associated with the completion of the asynchronous operation gets called and the JS in that callback runs. 然后,在某个时间之后,当异步操作完成并且JS解释器没有执行其他操作时,将调用与异步操作完成相关的回调,并且该回调中的JS将运行。


For more info on the event-driven nature of Javascript, see these: 有关Java事件驱动性质的更多信息,请参见以下内容:

How does JavaScript handle AJAX responses in the background? JavaScript如何在后台处理AJAX响应? and the 9 referenced links in that post. 以及该帖子中引用的9个链接。

Why does a while loop block the event loop? 为什么while循环会阻止事件循环?

setTimeout waits too long in Node.js setTimeout在Node.js中等待太长时间

Wait until flag=true 等到flag = true

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM