AlgoInsight

Free lesson · JavaScript Basic to Advance

Interview Lens: The Event Loop Question

Easy5 min read

The Question

"Can you explain the event loop?"

Sometimes phrased as: "What does this code log, and why?" followed by something like:

console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

This is the question that broke me in my second Google interview. I'll get to that in a moment.

Nikhil here. I'm taking this Interview Lens Topic instead of Emeka because the event loop sits squarely in my territory — runtime model, execution context, the mechanics underneath the language. Emeka covered the async material in Topics 9.1 through 9.10. I'll cover the interview-question form, with Arun joining for the interview-psychology angle.

The event loop question is the most-asked deep JavaScript interview question. More than closures. More than this. More than prototypes. Almost every senior or staff JavaScript interview includes some form of it. Get this right and the rest of the async section of your interview gets easier. Get it wrong and your credibility on async drops, which makes every Promise question harder.

Why They Ask It

The event loop tests three things at once. It tests whether you understand JavaScript's runtime model. It tests whether you can trace execution under pressure. And it tests whether you've actually studied the language or just learned to use it.

A candidate who can recite "JavaScript is single-threaded" has memorized a phrase. A candidate who can walk through "synchronous code first, then microtasks drained, then one task, then microtasks again" has the model. The difference is night and day in interviews.

Three signals interviewers listen for:

Signal 1: Do you know the precise rules, or do you approximate? "Promises run before setTimeout" is approximately true but not the precise rule. The precise rule is "microtasks drain between every task." Candidates who give the approximate answer reveal they've heard the result without understanding the mechanism.

Signal 2: Can you trace step by step? Given an output-prediction puzzle, do you walk through the code carefully — synchronous parts, then queue inspection, then microtask drain, then task run, then microtask drain again? Or do you guess based on rough memory of "Promises first"? Tracing carefully is the senior skill.

Signal 3: Can you handle the follow-up that goes one level deeper? "What if the task itself schedules a microtask?" "How is Node's event loop different from the browser's?" "Why does the microtask queue exist?" These follow-ups separate "studied for one question" from "actually understands the model."

The Wrong Answers

Warning: The event-loop mistakes interviewers see most often.

"JavaScript runs Promises before setTimeout." This is a result, not a mechanism. The reason isn't that Promises have some special status — it's that they go into the microtask queue, and the microtask queue is drained completely before any task. A candidate who gives the result without the mechanism signals shallow understanding.

"setTimeout(fn, 0) runs immediately." No. It schedules a task. The "0" is a minimum delay, not an actual one. The callback only runs after the current synchronous code finishes and the microtask queue is drained.

"All async work goes to the same queue." No. There are at least two queues — microtasks (Promises, queueMicrotask, await continuations) and tasks (setTimeout, setInterval, I/O callbacks, UI events). The two queues have different priorities. Treating them as one misses the central insight.

"The event loop is part of the JavaScript engine." It's actually part of the runtime environment (the browser or Node.js), not the engine itself. The engine (V8, SpiderMonkey, etc.) runs JavaScript code. The environment provides the APIs, queues, and event loop around it. Getting this wrong signals that the candidate hasn't studied the boundary between engine and environment.

"Async means parallel." No. Async means "not blocking the main thread." JavaScript itself is still single-threaded. The work happens in the environment, but the JavaScript callbacks still run one at a time on the single thread.

The Right Answer

Three layers depending on the depth being probed.

The 30-Second Answer

"JavaScript is single-threaded — one call stack, one thing running at a time. The event loop is the mechanism that pulls async work back into JavaScript when it's ready. There are two queues: the microtask queue, which holds Promise callbacks and similar, and the task queue, which holds setTimeout callbacks, I/O callbacks, and UI events. The event loop waits for the call stack to empty, drains the microtask queue completely, then pulls one task from the task queue. After each task, it drains microtasks again before pulling the next task."

That's the foundation. Stop there if the interviewer seems satisfied.

Interactive animation: The whiteboard drawing, rehearsed - the stack, the browser's side, two queues with microtasks draining first, and the loop.

The 2-Minute Answer

Build on the above:

"The reason this design exists is to keep JavaScript responsive without multi-threading complexity. Long-running work doesn't block the main thread — it's handed off to the environment (browser or Node), which does the actual I/O. When the work is done, the callback is queued and the event loop runs it on the main thread.

The microtask priority is a deliberate design choice. Promise resolutions need to feel 'fast' — like they happen immediately after the operation completes — so they go into the high-priority queue. setTimeout and similar timers go into the lower-priority task queue, with rendering and UI events able to happen between tasks.

This is why setTimeout(fn, 0) doesn't actually run fn immediately. The current synchronous code has to finish, then all pending microtasks have to drain, and only then does the setTimeout callback get a turn. This trips up developers who think the '0' means 'right now,' but it means 'after everything currently queued at higher priority.'"

This is the answer for a standard senior interview.

The Deep Dive

For staff-level interviews or when the interviewer probes further:

"The microtask queue is drained completely between every task, not just at the start. If a task schedules new microtasks, they run before the next task does. This is what allows recursive Promise chains to starve the task queue — if you keep adding microtasks, the task queue never gets attention. In practice this matters for UI responsiveness; a long chain of .then calls can prevent the browser from rendering or handling clicks.

Node.js has additional structure beyond the basic two-queue model. The Node event loop has phases — timers, pending callbacks, idle/prepare, poll, check, close callbacks. Each phase has its own callback queue. setTimeout callbacks go to the timers phase. setImmediate goes to the check phase. I/O completion callbacks go to the poll phase. The microtask queue is checked between each phase transition. This is more complex than the browser model but follows the same fundamental principle: microtasks have priority, tasks are processed one at a time with microtask drains between.

The await keyword in async functions is implemented as a microtask point. When an async function hits await, it suspends, and the resumption is queued as a microtask. So async/await isn't separate from the event loop — it's another way to use the microtask queue. This is why mixing await with setTimeout produces the same ordering as mixing .then with setTimeout.

This whole model exists because JavaScript was designed for environments where responsiveness matters more than raw throughput. A single-threaded language with cooperative async is simpler than a multi-threaded one. The event loop is the mechanism that makes this simplification work."

This level of answer signals real depth. Use it when the interview is at a level where staff engineers are being evaluated.

Interactive animation: The senior-screen composite - an async function runs sync up to its await, so "end" logs before both microtasks.

Walking Through the Classic Puzzle

The canonical interview puzzle, traced step by step:

console.log("1");

setTimeout(() => console.log("2"), 0);

Promise.resolve().then(() => console.log("3"));

console.log("4");

Out loud:

"First, synchronous code runs. console.log('1') prints 1. The setTimeout schedules a task — the callback goes into the task queue. The Promise.resolve().then schedules a microtask — that callback goes into the microtask queue. Finally, console.log('4') prints 4.

Now synchronous code is done. The event loop checks the microtask queue. It finds the Promise callback. It runs it. Output: 3. The microtask queue is now empty.

The event loop checks the task queue. It finds the setTimeout callback. It runs it. Output: 2.

Total output: 1, 4, 3, 2. The reason 3 comes before 2 is the microtask priority — the microtask queue is drained before any task runs."

If you can verbalize this trace cleanly, you've signaled mastery of the event loop. Practice it out loud until it's automatic.

Interactive animation: The output quiz in rungs - a timer, then a promise chain, then async/await - and the rule each rung probes.

The Follow-Ups

Interview: What the interviewer asks next.

"What if the setTimeout callback itself schedules a Promise?" The new Promise's .then goes into the microtask queue. After the setTimeout callback finishes, the microtask drains happen — so the new .then runs before the next task.

"What's the difference between setTimeout(fn, 0) and Promise.resolve().then(fn)?" Both run after the current synchronous code. But Promise.resolve().then is a microtask, scheduled with high priority. setTimeout(fn, 0) is a task, scheduled with lower priority. If you have both, the Promise callback runs first.

"How does Node's event loop differ from the browser's?" Node has phases (timers, pending, idle/prepare, poll, check, close). Browser has a simpler model. Both have microtask drains between scheduling points. Most JavaScript code works the same in both, but specific timing edge cases differ.

"What's process.nextTick in Node?" Even higher priority than microtasks. Runs before regular microtask processing. Node-specific. If asked, mention it briefly — most browser developers won't encounter it.

"Why does the microtask queue exist?" For Promise responsiveness. Promise-driven reactive code feels faster when its callbacks run as soon as possible after the current synchronous code, without waiting for timer-style delays. The microtask priority enforces this.

The Founder Moment

The interview that broke me on this question was my second Google attempt, in 2017. I'd cleared coding. Onsite started fine. Then the System Design interviewer asked me to walk through what happens when you call setTimeout(fn, 100) in a browser.

I gave the surface answer: "It runs fn after 100 milliseconds." He waited. I tried again: "The browser schedules a timer." Wait. "The callback goes into a queue when the timer fires." He nodded slightly, waiting for more. "Then the event loop picks it up..."

I trailed off. I'd built distributed systems at Razorpay. I'd shipped payment systems handling 800 crore rupees per day. But I'd never had to explain the mechanism of setTimeout at the level Google wanted. I'd used it thousands of times. I'd never had to talk about it.

I walked out of that interview, sat in the Hyderabad parking lot of the Google office, and cried. Not because of one bad question. Because I realized I'd been building real systems for years on top of a language whose runtime model I couldn't fully articulate when someone asked. I knew it worked. I didn't know why.

I spent the next eighteen months actively learning the things I'd been using on faith. The event loop was one of them. I'd read the V8 source. I'd read the HTML spec on the task queue. I'd built sample apps with weird timing puzzles and traced through them.

The third Google interview, two years later, included the event loop question. I gave the deep-dive answer. The interviewer's note in the feedback I saw later said "exceptional clarity on runtime model." That was the loop where I got the offer.

What I took from those years: you cannot fake the event loop in a senior interview. The interviewer can tell within thirty seconds whether you've studied it or you're guessing. The good news: it's a finite topic. There are maybe twenty questions interviewers can ask about it, and the answers are knowable. Spend the time.

The other thing I took: any moment of failure can be the start of two years of growth. The parking lot at Google was the worst day of my career and the best one. Without it, I'd still be the engineer who'd used JavaScript without understanding it.

Master the event loop. The rest of async follows.

Back to JavaScript Basic to Advance