Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Asynchronous JavaScript  ›  Lesson

The Event Loop

Event Loop 12 minJavaScript runs on one thread with a single call stack; the event loop hands it work from two queues once the stack is empty — the microtask queue (Promise callbacks), which is always fully drained first, then one task from the macrotask queue (setTimeout, setInterval) — which is why a 0ms setTimeout still runs after every already-queued Promise.

The Event Loop

In short: JavaScript runs on one thread with a single call stack — it can only do one thing at a time. Async work doesn't run in parallel on that thread; it gets scheduled onto one of two queues and handled once the stack is empty. The microtask queue (Promise .then/.catch/ .finally) is always drained completely before the event loop touches the macrotask queue (setTimeout, setInterval) — which is why a setTimeout with a 0ms delay still runs after every microtask that was already waiting, not before.

One chef (the call stack) doing one order at a time, fed by two trays: the microtask tray (Promise callbacks), emptied completely before the next tray, and the macrotask tray (setTimeout, setInterval), one order taken per round — a note that even setTimeout(fn, 0) waits behind every microtask already queued

Sync first, then microtasks, then macrotasks

The ordering rule, shown directly — nothing here is random:

javascript

"1" and "2" are plain synchronous code — the chef does them immediately, in order, before anything else is even considered. Once the call stack is empty, the microtask queue is checked first: "3" runs. Only after the microtask queue is fully empty does the event loop take one task from the macrotask queue: "4". The setTimeout's 0ms delay never changes this order — it only means "eligible to run as soon as possible," not "before the microtasks."

setInterval: the same macrotask tray, repeatedly

setInterval schedules its callback onto the macrotask queue over and over, at roughly the given interval, until you clearInterval it:

javascript

Each tick is its own trip through the macrotask tray — the chef finishes whatever else is happening, then takes the next tick's order. Forgetting clearInterval is a classic bug: the interval keeps firing forever, long after you needed it.

Quick practice

quick drill

Which runs first once the call stack is empty: the microtask queue or the macrotask queue?

quick drill

Does setTimeout(fn, 0) run its callback before or after an already-pending Promise.then() callback?

quick drill

What does forgetting to call clearInterval on a running setInterval cause?

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainWhy the promise wins the race
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");

This logs 1, 4, 3, 2 — not 1, 4, 2, 3, even though the timeout says 0ms. Explain why, using the call stack and the two queues.