The event loop
The flagship page — stack, queues, and the order everything runs in.
One thread runs your code; everything slow waits in queues, and the loop feeds them onto the stack when it's free. This page is the most-asked JavaScript interview topic because the sync-timer-promise order puzzle appears in screens, onsites and take-homes alike.
Real-life situation
A dashboard logs "loading", fires a data fetch, schedules a refresh timer and logs "ready". Users report the timer beating the data although both were "immediate". The cause is queue priority, not speed: the promise reaction drains as a microtask right after the script, while the zero-delay timer waits a full loop turn. Knowing the order turns a mystery into a schedule.
Seen in: Node.js documents its loop phases, timers and nextTick ordering
in the official event loop guide (Node.js, 2009,
event loop guide).
Visual
Sync, promise, timerbrowser
Call stack
Tasks
empty
Microtasks
empty
Console
Print "Take the order". console.log prints "Take the order".Why: The current operation executes synchronously and appends this value.
console.log("Take the order");
setTimeout(() => console.log("Start the oven timer"), 0);
Promise.resolve().then(() => console.log("Confirm the order on screen"));
console.log("Hand over the receipt");Try it
console.log("script start");
setTimeout(function () {
console.log("timer");
}, 0);
Promise.resolve().then(function () {
console.log("promise");
});
console.log("script end");
async function load() {
console.log("load start");
await sleep(10);
console.log("load done");
}
load();
console.log("after call");
// Timers wait a full turn; promises drain first.Expected output
script start script end load start after call promise timer load done
Try changing the timer delay to 20 and predict before running. Then move
the promise below load() and check the order holds.
Browser versus Node, briefly
Browsers add a render step per turn; Node runs timer, I/O and check phases
per iteration. Both drain microtasks the same way. Node-only extras:
process.nextTick (before even promises) and setImmediate (next check
phase). Same mental model, different machinery.
The classic trio — guess first, then reveal.
console.log("a");
setTimeout(function () {
console.log("b");
}, 0);
Promise.resolve().then(function () {
console.log("c");
});Interview questions
What is the event loop, in one paragraph?basic
JavaScript runs one thing at a time, so waiting is delegated: timers, network and events register callbacks and free the thread. The event loop is the permanent scheduler asking "is the stack empty, and is anything due?" — running due callbacks, then microtasks, then checking for render, forever. Async code is just callbacks the loop runs later; nothing ever runs in parallel with your code.
Interviewers also ask
- What runs first after the current script: a timer or a promise?
What is the difference between the task queue and the microtask queue?intermediate
Both hold callbacks, but microtasks (promise reactions, queueMicrotask) drain COMPLETELY after every task before anything else — including render. Tasks (setTimeout, events, network callbacks) run one per loop turn. Consequence: a promise scheduled alongside a timer always wins, and a microtask that keeps queueing microtasks starves rendering. "Microtasks VIP-drain, tasks take turns" is the sentence to remember.
Interviewers also ask
- Can microtasks starve rendering? How?
In what order do sync code, a promise and a setTimeout run?basic
Sync first (it owns the thread), then the promise reaction, then the timer — even with zero delay, because the timer must wait a full loop turn while the microtask drains immediately after the script. This exact trio is the most-asked output puzzle in interviews: say the order, then say WHY (microtask drain versus next-task wait), and you have full marks.
Interviewers also ask
- What changes with setTimeout(..., 0)?
Does setTimeout(fn, 0) run immediately?intermediate
No — "0ms" means "no sooner than one full loop turn from now". The callback waits behind the current script, the entire microtask drain, and any earlier-due tasks; browsers also clamp nested timeouts to ~4ms. It is a deferral tool (let the page paint first, break up work), never a precise scheduler. For "run ASAP but async", queueMicrotask or a resolved promise is earlier and more honest.
Interviewers also ask
- What is the 4ms clamping rule?
How is the Node event loop different from the browser's?intermediate
Same idea (one thread, queues, phases), different machinery: Node runs timers, I/O callbacks, and immediates in ordered phases per iteration, while browsers schedule tasks plus a render step. Node adds process. nextTick (drains before even promises) and setImmediate (next iteration's check phase). Interview-safe summary: browser loops to paint; Node loops through I/O phases — and nextTick beats promises only in Node.
Interviewers also ask
- What runs first in Node: nextTick or a promise?
Why is JavaScript called single-threaded if browsers do many things at once?basic
YOUR code gets one thread: a single call stack running one statement at a time. The parallelism lives OUTSIDE it — the browser's network, timers and rendering run on their own threads and just post callbacks for your thread to run later. So the page multitasks while your code never truly overlaps itself, which is why shared-state races are rare and blocking the thread freezes everything.
Interviewers also ask
- What actually runs on other threads?
What does await actually pause?intermediate
Only the async function it sits in — which returns to its caller at that point (as a pending promise) while the rest of the program continues. The thread is never blocked; the function's remaining lines are scheduled as a microtask when the awaited value settles. "Await parks the function, not the thread" resolves every "is async/await blocking?" trap.
Interviewers also ask
- Is anything blocked while awaiting a fetch?
How would you prove a timer callback waits for slow sync code?intermediate
Schedule setTimeout(..., 0), then burn 200ms synchronously: the callback fires late, after the blocking code, because the loop cannot run it while the stack is busy. Timers guarantee "no sooner than", never "exactly at". This demo doubles as the performance moral — slow sync code delays everything due, including paint and input.
Interviewers also ask
- What guarantees do timers actually give?
Key takeaways
- Sync runs first, microtasks drain completely, then one task per turn.
- Zero-delay timers still wait a full turn; promises don't.
awaitparks the function, never the thread.
References
Last verified: 2026-10-12.
Runner helpers
sleep(ms)waits;fakeFetch(url)returns deterministic sample data. Console code runs in a worker with a 5-second limit.