One interview · 2 questions I should have nailed
-
What are closures?
Where I slipped: I answered "a function inside a function." That just describes a nested function — basically a callback — not a closure. I even framed it as a function that takes a parameter, which isn't the point at all. Then I really went for it and called array methods like map, forEach and reduce "examples of closures" — they're not. Those are higher-order functions; the callback you hand them is only a closure if it captures variables from the surrounding scope. The part I missed is that a closure is defined by retaining state from its enclosing scope, not by being nested.
The answer I should've given
A closure is a function bundled together with references to its surrounding lexical scope. The defining trait isn't the nesting — it's that the inner function keeps access to the outer function's variables even after the outer function has returned, and can read and update that captured state across calls. A nested function (or a callback) only becomes a closure once it actually closes over (remembers) state from its enclosing scope. That retained, private state is the whole point. And array methods like map, forEach or reduce aren't closures either — they're higher-order functions (functions that take a callback); the callback is a closure only when it reaches for variables outside itself.
function counter() { let count = 0; // captured by the closure return () => ++count; // remembers `count` after counter() returns } const next = counter(); next(); // 1 next(); // 2 ← state persisted via the closureClosuresLexical scopeHigher-order functionsJavaScript -
Is JavaScript single-threaded or multi-threaded?
Where I slipped: I said "single-threaded," then tried to back it up with the event loop and fumbled it. I called the event loop "a queue" and stopped there — true, but I never explained why it's a queue, how it actually works, or how that ties back to there being a single thread. A vague half-answer lands worse than a confident "yes, single-threaded" would have.
The answer I should've given
JavaScript runs on a single main thread with one call stack, so it executes one thing at a time. The event loop is what keeps that single thread responsive: it runs the current task to completion, while anything asynchronous — timers, I/O, fetch, DOM events — is handed off to the host (the browser, or Node via libuv), which does that work elsewhere and pushes a callback onto a queue when it's done. There are actually two tiers: the macrotask queue (setTimeout, I/O, UI events) and the microtask queue (Promise callbacks, queueMicrotask). The loop only pulls the next callback once the call stack is empty, and it drains all microtasks before the next macrotask. That ordering — and the fact that one long synchronous block freezes everything — is exactly why it's a queue and not parallel execution. For real parallelism you step outside the language: Web Workers (browser) and Worker Threads (Node) run on separate OS threads and communicate by message passing, with SharedArrayBuffer + Atomics for shared memory. So: single-threaded execution model, concurrency via the event loop, true multithreading via workers.
console.log('1: sync'); // runs now, on the stack setTimeout(() => console.log('4: macrotask'), 0); // queued as a macrotask Promise.resolve().then(() => console.log('3: microtask')); console.log('2: sync'); // → 1, 2, 3, 4 // stack empties → drain ALL microtasks (3) → then the next macrotask (4)Event loopMicrotasksWeb WorkersConcurrency