Interview Q&A / JavaScript

IntermediateIn-depth2 min readUpdated 30 Sept 2026

How do tasks and microtasks work in the JavaScript event loop?

SHORT ANSWER

JavaScript runs one piece of code at a time. After the current code finishes, it runs all waiting microtasks (like promise callbacks) before the next task (like a setTimeout callback).

Explain it simply

Imagine a single cashier at a chai stall. The cashier serves one customer at a time (the call stack). There are two waiting lines. The VIP line is for microtasks, such as promise callbacks. The normal line is for tasks, such as setTimeout callbacks, clicks and network events. Every time the cashier finishes with a customer, they first serve everyone in the VIP line, and only then call the next person from the normal line.

That is why a promise callback usually runs before a setTimeout callback, even when the timeout is zero.

Example

js
console.log("start");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("promise"));
console.log("end");
// Output: start
// Output: end
// Output: promise
// Output: timeout

The two normal console.log calls run first because they are part of the current code. Then the microtask queue runs (the promise). Only after that does the event loop take the next task (the timeout).

How it works

  1. The engine runs the current script until the call stack is empty.
  2. It drains the microtask queue completely: promise then/catch/finally callbacks, queueMicrotask, and the code after an await.
  3. The browser may render the page.
  4. It takes one task from the task queue (timer, click, message) and repeats.

Because microtasks are drained completely, a microtask that keeps adding more microtasks can block rendering and freeze the page.

Real-world use

  • Knowing why await code continues "soon" but after the current function.
  • Debugging why state appears updated in a different order than expected.
  • Splitting heavy work with setTimeout so the page stays responsive.

Common mistakes

  • Believing setTimeout(fn, 0) runs immediately. It waits for the current code and all microtasks.
  • Assuming await pauses the whole program. It pauses only that async function; other code keeps running.
  • Creating endless microtask loops that starve rendering.

What to say in the interview

"JavaScript has a single call stack and an event loop. After each task finishes, the engine drains the whole microtask queue, which holds promise callbacks and code after await, before it picks the next macrotask such as a timer or click. So Promise.resolve().then logs before setTimeout(..., 0). This matters for ordering bugs and for keeping the UI responsive, because long microtask chains can block rendering."

Follow-up questions

  • Where does await fit into the queues?
  • What is queueMicrotask and when would you use it?
  • How is Node's event loop different from the browser's?

Go deeper

Read these once the basics above feel easy.

Node.js adds phases (timers, I/O callbacks, check for setImmediate, close callbacks) and a separate process.nextTick queue that runs even before promise microtasks. In the browser, requestAnimationFrame callbacks run just before painting, which makes them the right place for animation work.

#event-loop#promises#async

Related questions

Practise more JavaScript questions

Test your recall with short answers, then return to the detailed explanations when you need context.

Open JavaScript quick answers →

Get new questions like this by email

One short email when something new and useful lands. No spam, unsubscribe in one tap.

By subscribing you agree to the Privacy Policy.