DEV3 Technologies home

DEV3 Developer Forum

Why do all my setTimeout callbacks print the same number in a for loop?

JavaScript helpSolved0 views
dev3 ·
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

This prints 3, 3, 3 instead of 0, 1, 2. What is happening?

#javascript#closures#loops

0

1 reply

dev3 · Accepted answer

var is function-scoped, so there is only one i shared by all three callbacks. The loop finishes (leaving i === 3) before any timer fires, and each closure reads that same variable.

Fix 1: use let

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

let in a for header creates a fresh binding for every iteration, and closures capture that iteration's copy.

Fix 2: pass the value

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

setTimeout forwards extra arguments to the callback.

Fix 3: IIFE (legacy code)

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

What a closure is

A function plus the variables from the scope where it was created. It keeps them alive and can read or change them later.

function counter() {
  let n = 0;
  return () => ++n;
}
const next = counter();
next(); // 1
next(); // 2

n is private: nothing outside can touch it.

Pitfalls

  • Closures keep their whole captured scope reachable, so a long-lived callback (event listener) holding a big array prevents garbage collection. Remove listeners when done.
  • Same trap exists with event handlers created in loops using var.
  • Prefer const by default, let when you must reassign, avoid var.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.