let is block-scoped and creates a fresh binding per iteration. Each callback closes over its own i. var is function-scoped; all callbacks share one i, which is the final value by the time they run.
Fixing the setTimeout Closure Bug Three Ways in JavaScript
Why does let fix the setTimeout closure bug but var does not in JavaScript?
The answer
More FAQs in Fixing the setTimeout Closure Bug Three Ways in JavaScript
Use let (fresh binding per iteration), use an IIFE (new function scope with pass-by-value parameter), or use setTimeout's extra arguments (pass the current value as an argument to the callback).
let. It is the cleanest and most readable. It creates a fresh binding per iteration without extra nesting or arguments. Use it in all ES6+ code.
The IIFE creates a new function scope per iteration and passes i as a parameter (j). Primitives are passed by value, so j is a copy of i at that iteration. The closure captures j, not i.
Still have questions?
Browse all our FAQs or reach out to our support team
Want to upskill yourself?
Your learning library
Small steps. Big possibilities.
Find your next course. Build something great.