Advanced JavaScript · 2 min read
Closures and the Event Loop
Lexical scope and closures, private state, the classic loop bug, and how the call stack, microtasks and macrotasks decide execution order.
Two ideas explain most "why does JavaScript do that?" moments.
Closures
A closure is a function together with the variables in scope where it was created. Those variables stay alive as long as the function does:
js
function makeCounter() {
let count = 0; // private: nothing outside can touch it
return {
inc: () => ++count,
get: () => count,
};
}
const a = makeCounter(), b = makeCounter();
a.inc(); a.inc(); b.inc();
a.get(); // 2
b.get(); // 1 (each call creates a new scope)The classic loop bug
js
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3 3 3
for (let j = 0; j < 3; j++) setTimeout(() => console.log(j)); // 0 1 2var is function-scoped, so all three callbacks close over the same i, which is 3 when they run. let creates a fresh binding per iteration.
The event loop
JavaScript runs one thing at a time on a single call stack. Slow work (timers, network) is handed to the browser. When it finishes, a callback is queued:
- Run the current script until the stack is empty.
- Run every microtask: promise reactions (
.then, code afterawait) andqueueMicrotask. - Render if needed.
- Take one macrotask (a
setTimeoutcallback, an event, a message) and go back to 1.
Predict the output
<p>Predict the order, then press Run.</p>
<button id="run">Run</button>
<ol id="out"></ol>Edit this example in the Playground.
Even with a 0 ms delay, setTimeout runs after all promise callbacks, because microtasks always drain first.
Why it matters for dApps
await wallet.signAndSendTransaction(...)frees the thread while the user decides; the UI keeps animating.- React state updates, promise chains and RPC polling all follow these ordering rules.
- Closures power hooks, event handlers, debounce and memoization.
Summary
A closure remembers its birth scope; use it for private state. The event loop runs the stack, then all microtasks, then one macrotask. With those two models you can predict any async ordering puzzle, including the ones in the final exam.
Saved in this browser.