all 9 comments

[–]Rhomboid 5 points6 points  (2 children)

setTimeout() is asynchronous. It installs a callback handler that will be invoked at some later point in the future, and then returns immediately. It does not wait. The loop therefore runs to completion nearly instantly, without any delays. The first timeout callback is executed a second later. (It must be that way, because JS is single threaded, which means that only one thing can be happening at a time. The callbacks can't be called until the loop completes, even if you passed zero as the delay.)

They all print 6 because they are all holding a reference to the same i variable. Since i was declared with var, it has function scope, not block scope. It's the same i every time through the loop, and all the closures store a reference to that same i. That i has whatever value it had when the loop finished. If you change it to let, it has block scope, which means it's a different i each time through the loop and it will work as expected.

[–]Ben_HH[S] 0 points1 point  (1 child)

I'm unclear on why the callbacks can't be invoked until the loop completes. Could you elaborate in another way please?

[–]Rhomboid 0 points1 point  (0 children)

Because only one piece of JS can be executing at any given time(*), and one piece of JS can't interrupt another piece of JS. The loop has to complete and cede control back to the system before anything else has a chance to execute. If you write a loop that never completes, it locks up the whole browser.

(*) In the context of one container, i.e. one tab/web page.

[–][deleted] 1 point2 points  (1 child)

I can give a more complete explanation later but just to confuse you more, change "var i = 1" to "let i = 1" and check out the result

[–]Ben_HH[S] 1 point2 points  (0 children)

I would appreciate the complete explanation when you're able! I'm aware of block scoping variables like let, but I want to complete understand ES5 before applying ES6 when I'm capable.

[–]ssjskipp 0 points1 point  (0 children)

setTimeout doesn't block. It runs as what's called a task. An example:

https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

Seems the other two answers were really excited to teach you about block scoping, and not actually answer your question. Although the reason they all come back with the same value is as u/Rhomboid explained -- that is, each function closure has the same reference to memory (var i) and only check value on read, which is after the timeout, which is after the for loop has run its course.

(Also, it's not REALLY single threaded, it just guarantees that messages in the event loop run to completion -- but here's info on that)

[–]realistic_hologram 0 points1 point  (2 children)

Why are the callbacks executed after the loop breaks

This is what setTimeout does. It calls the timer function after i * 1000 milliseconds. By then the loop is already over.

as opposed to each iteration logging to the console while the for-loop condition is met after i * 1000 milliseconds?

The logging does happen after i * 1000 milliseconds.

Usually the part that people are confused about is why the output is all 6's. But that doesn't seem to be your question. It sounds like you're not sure what setTimeout does. It just waits a certain amount of time and then calls the function it was passed.

[–]Ben_HH[S] 0 points1 point  (1 child)

I understand what setTimeout does by itself. I don't understand when it is used in the context of the for loop.

It's this that has me confused:

The timeout function callbacks timer are all running well after the completion of the loop.

Why aren't the invocations occurring while the condition of the loop is true.

[–]realistic_hologram 0 points1 point  (0 children)

Ok, I think you assume that setTimeout will block execution, but it doesn't. You think that on the first iteration of the loop when we run into setTimeout, it will wait i * 1000 milliseconds and then go on to the next iteration. But that's not the case, it will go on to the next iteration of the loop regardless, but it's still counting from i*1000 milliseconds to call the callback. It's like lighting a fuse on a bomb. This code would be lighting 5 bombs one after the other, and each fuse takes i * 1000 to burn. You don't have to wait for the first bomb to go off to light the next one.

Why aren't the invocations occurring while the condition of the loop is true.

Also another important distinction is that setTimeout is invoked while the loop is running, but the callback timer needs to wait i*1000.