All topics
Asyncadvanced

Microtasks vs Macrotasks

The two distinct queues the event loop draws from, and why microtasks always run before the next macrotask.

JavaScript's event loop has at least two queues, and the strict priority ordering between them explains a large chunk of surprising async output ordering. Getting the mental model exactly right is a clear signal of deep async understanding.

Macrotasks are like scheduled factory shifts. Microtasks are urgent messages handled instantly mid-shift, and every new urgent message is handled too before the next shift starts.

Key Concepts

1
Macrotasks include setTimeout/setInterval callbacks, I/O callbacks, and UI rendering. Microtasks are a separate, higher-priority queue holding .then/.catch/.finally callbacks and queueMicrotask calls.
2
The event loop runs one macrotask, then, before touching the macrotask queue again, drains the entire microtask queue — including new microtasks scheduled during that drain — before moving on. This means microtask chains can starve the macrotask queue if they keep scheduling more microtasks indefinitely.
3
A Promise.resolve().then(cb) scheduled at the same moment as setTimeout(cb, 0) will always run first, because the entire microtask queue empties before the event loop considers the next macrotask.