2

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). My questions:

1.What are the values of i*3000? Is it :3000,6000,9000 OR 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?

3.var i is declared in the for loop. So, when for loop is finished and closed it should be deleted from the scope. How can setTimeout functions have access to its value then?

Yozex
  • 73
  • 8
  • `when for loop is finished and closed it should be deleted from the scope` this is wrong. `i` is only marked for GC when there are no longer available references to it from anywhere in the program. Since the queued `setTimeout`s still have access to it, it is not marked for GC until all of them are fired. – Patrick Roberts Oct 12 '18 at 18:08
  • https://johnresig.com/blog/how-javascript-timers-work/ – Train Oct 12 '18 at 18:10
  • 1
    I'm nominating this question for reopening as I believe that OP has already identified the issue from the duplicate: *"I understand that by the time setTimeout functions are called, that the for loop is already done (with value i=4)."* OP is asking at what point intervals are defined, where `setTimeout` is stored, and how garbage collection works with stored `setTimeout`s. None of this is covered by the marked duplicate. – Tyler Roper Oct 12 '18 at 18:15
  • Thanx for the point. I have seen all the solutions for this problem already, I am just interested in the way it functions, and the reasons for it. – Yozex Oct 12 '18 at 18:25
  • I am not sure yet about setTimeout function. From which scope is it called , after the for loop exits? – Yozex Oct 12 '18 at 18:25
  • `setTimeout()` is called right from within your loop. It's non-blocking. All it does is register a timer inside the JS engine and then immediately returns so the `for` loop can continue to run. Perhaps what is confusing you is the non-blocking nature of `setTimeout()`. If you think of it more like setting a reminder in your calendar. You set up the reminder in your calendar and then you go about your business and sometime later, the calendar will notify you that your reminder has occurred. That's how `setTimeout()` works. – jfriend00 Oct 12 '18 at 18:30

1 Answers1

3

1.What are the values of i*3000? Is it :3000,6000,9000 OR 3000,3000,3000?

Your alerts will show 4,4,4 (the value of i after the loop is done).

Your setTimeout() calls will get fed a time value of 3000, 6000, 9000. setTimeout() is non-blocking. So, the timer is set and then the for loop continues.

The problem here is that your alert() runs AFTER the for loop is done. So, you will be alerting the value of i at the end of the for loop. If you change to use let as in:

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.

Also, please switch to using console.log(i + " second(s) elapsed"); as alert() can mess with timing of things (it doesn't here, but it can).

2.setTimeout function is in the for loop.If it is called after the loop has finished and closed where is it actually stored?

It's not clear what you mean by "actually stored". setTimeout() is a system function built into the core of the Javascript engine. Active timers are stored inside of the JS engine.

3.var i is declared in the for loop. So, when for loop is finished and closed it should be deleted from the scope. How can setTimeout functions have access to its value then?

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.

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.

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. 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.

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.

Note, this is very different than thinking about the lifetime of a variable in a typical stack frame world like 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.

Notes on non-blocking setTimeout()

It appears you may be confused about how multiple setTimeout() calls can be called within the loop, but the loop keeps running. This is because setTimeout() is non-blocking. That means it registers a timer internal to the JS engine and then immediately returns allowing your for loop to continue to run. 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.

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. Then, right before your 4pm appointment, the calendar notifies you of the upcoming appointment. 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. 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. 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.


For more info on the event-driven nature of Javascript, see these:

How does JavaScript handle AJAX responses in the background? and the 9 referenced links in that post.

Why does a while loop block the event loop?

setTimeout waits too long in Node.js

Wait until flag=true

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 1
    **1.** is about the `i * 3000` passed as argument to the `setTimeout` function. Because of the `I understand that by the time setTimeout functions are called, that the for loop is already done (with value i=4).` the OP it as least partially aware what you described for **1.** – t.niese Oct 12 '18 at 18:10
  • Alternatively, you can use `for (var i = 1; i <= 3; i++) { setTimeout(function (i) { alert(i + " seconds(s) elapsed"); }, i * 3000, i); }`, which explicitly creates a separate instance of the variable for each iteration, rather than allowing `let` to implicitly do so. – Patrick Roberts Oct 12 '18 at 18:17
  • @PatrickRoberts - Yes, there are lots of ways to work-around it (I could probably name at least 3 different work-arounds). But, certainly `let` is the cleanest in any modern JS engine. – jfriend00 Oct 12 '18 at 18:19
  • @Yozex - Added a bunch more info to my answer on garbage collection and how `setTimeout()` is non-blocking. – jfriend00 Oct 12 '18 at 18:35
  • One more question though. I will use your analogy. If I have a reminder for 4pm, what would happen if I am doing something else at that time? Would a reminder fire anyway, or would it wait until, let`s say,4:05pm when I am finished with what I am doing? – Yozex Oct 12 '18 at 18:52
  • @Yozex - Javascript in the browser is an event driven implementation. When the timer fires an event is inserted in the event queue. If the JS interpreter is not doing anything at the time, the event is processed immediately. If the JS interpreter is busy at the time, the event is not processed until whatever it was doing is done and it can then look in the event queue for the next event to process. This is often called "event-driven, single threaded". So, in your analogy, the reminder wouldn't fire until you were done with what you were doing and could check the queue again. – jfriend00 Oct 12 '18 at 18:56
  • @Yozex - For this reason, timers are approximate in Javascript. They don't interrupt something else that's running. They will not run before their appointed time, but they might not run until sometime after their time if the JS engine was busy at the time they fired. – jfriend00 Oct 12 '18 at 18:57
  • Understood. Great way of explaining things, well done! – Yozex Oct 12 '18 at 19:01
  • @Yozex - I added a several references on the Javascript event loop to the end of my answer. – jfriend00 Oct 12 '18 at 19:12
  • Every newly acquired knowledge raises new questions :-). So: 1. why is it working with let, and not with var (is it just the change in the engine with es6, or is it more to it)? 2. If a browser starts loading some script from the server, then it wouldn`t be able to do anything else, since it has to be finished until anything else goes into the event queue, right? 3. Is it possible to load multiple , for example, pictures from the server onto the page at the same time? If yes, how come, since it can only do one thing at the time? – Yozex Oct 12 '18 at 19:20
  • @Yozex - As I said in my answer, `let` works differently than `var`. `let` is block-scoped. `var` is function scoped. And, `let` has a special feature for `for` loops that make a unique copy of the variable for each iteration of the loop. This is part of the developments in the ES6 version of EcmaScript (e.g. Javascript). – jfriend00 Oct 12 '18 at 20:04
  • @Yozex - A browser runs your Javascript as single threaded (except for webWorkers which are not involved in this discussion so far). It's own native code inside the browser implementation can do multiple things at once such as load multiple scripts and multiple images at once and, in fact, it does do that. – jfriend00 Oct 12 '18 at 20:05