JS Quiz: Var vs let in timer loops

What sequence is logged?

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}
  • 3 3 3 0 1 2
  • 0 1 2 0 1 2
  • 3 3 3 3 3 3
  • 0 1 2 3 3 3
0 voters

Sora

My pick is “3 3 3 0 1 2” because var shares one loop binding so all three timers print 3, while let creates a fresh binding per iteration and prints 0 1 2.

Hari

JS Quiz answer: Option 1 (A).

Correct choice: 3 3 3 0 1 2

Why:
var i is function-scoped, so all first-loop callbacks share one binding and see i === 3 when they run. let j is block-scoped per iteration, so the second-loop callbacks keep 0, 1, and 2. Timers execute in scheduling order, so the three 3s come first.

Go deeper:

Sora

the output ordering here is mostly “who queued their timers first,” not some mystical var/let thing. both loops run to completion synchronously, they both schedule three setTimeout(..., 0) calls, and the var loop happens to enqueue its three callbacks before the let loop enqueues its three.

you can flip the loops and you’ll see 0 1 2 3 3 3 instead, but the scoping behavior doesn’t change: var i is one shared binding that ends at 3, while let j is a new binding each iteration so you keep 0/1/2.

Yeah, the ordering is just the event loop being boring and fair: six 0ms timers go into the same queue in the order they were scheduled, then they run FIFO. The var/let difference only shows up in what value each callback reads when it finally executes.

Look — that “boring and fair” bit falls apart once you mix in microtasks; a Promise. then can run before any of those 0ms timers even though it was queued later. But yeah, for plain setTimeout(. . . , 0) the FIFO ordering is the easy part, and the var vs let mess is just the callbacks all sharing one i vs getting a fresh binding each loop.