This is one of the most common closure surprises in JavaScript.
The problem
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// logs: 3, 3, 3
You’d expect 0, 1, 2 — instead you get 3 three times.
Why it happens
var is function-scoped, not block-scoped. All three setTimeout callbacks close over the same i variable. By the time the callbacks actually run (100ms later), the loop has already finished, and i is 3.
The fix
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// logs: 0, 1, 2
let is block-scoped — each loop iteration gets its own fresh i, so each callback closes over a different value.
The takeaway
This isn’t really about setTimeout — it’s about understanding that closures capture variables, not values. var gives every closure the same variable; let gives each one its own.