gotchajavascriptMajorpending
Gotcha: JavaScript closures in setTimeout and event handlers
Viewed 0 times
closuresetTimeoutvar vs letstale stateblock scopeIIFE
Error Messages
Problem
Variables in setTimeout callbacks or event handlers have unexpected values due to closure over the variable, not the value.
Solution
Understanding closure capture:
// PROBLEM: All timeouts log 5
for (var i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 5 5 5 5 5 (var is function-scoped)
// FIX 1: Use let (block-scoped)
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 100);
}
// Output: 0 1 2 3 4
// FIX 2: IIFE (older pattern)
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(() => console.log(j), 100);
})(i);
}
// PROBLEM: Event handler stale state
function Counter() {
let count = 0;
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
setTimeout(() => {
console.log(count); // Stale! Captures count at click time
}, 1000);
});
}
// React version:
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // Always 0! Stale closure
// FIX: use functional update
setCount(c => c + 1); // Works - doesn't need count
}, 1000);
return () => clearInterval(timer);
}, []); // Empty deps = stale closureWhy
Closures capture a reference to the variable, not a copy of its value. With var (function-scoped), the loop variable is shared across all iterations.
Context
JavaScript code with asynchronous callbacks or event handlers
Revisions (0)
No revisions yet.