Skip to main content

Loop with var and setTimeout

Because of two things at once:

  1. var has function scope. In the loop for (var i = 0; i < 3; i++) { ... }, only one i variable is created for the whole function, not a new one per iteration. Every setTimeout callback closes over the same value of i.
  2. setTimeout runs later. Callbacks land in the macrotask queue and run after the current JS stack empties out. By the time the first timer fires, the loop has already finished, and i holds its final value (for example, 3).

The symptom

javascript
for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); } // 3 // 3 // 3

How to fix it

Option 1 - let (block scope):

javascript
for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 0); // 0, 1, 2 }

Option 2 - an IIFE/closure that passes the current i:

javascript
for (var i = 0; i < 3; i++) { (function (j) { setTimeout(() => console.log(j), 0); // 0, 1, 2 })(i); }

Option 3 - pass it as an argument to setTimeout:

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

Option 4 - bind:

javascript
for (var i = 0; i < 3; i++) { setTimeout(console.log.bind(null, i), 0); // 0, 1, 2 }

In short: var gives you one "shared" variable for the whole loop, and setTimeout runs after the loop has already finished. Use let, or isolate the value through a closure or an argument.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.