Microtasks vs macrotasks
Microtasks and macrotasks are different task queues that are processed at different moments inside the Event Loop. Macrotasks are the "big" events: timers, network requests, DOM events. Microtasks are the "instant" jobs that run right after the current code but before the next macrotask.
Theory
TL;DR
- These are two separate queues: the
Task Queuefor macrotasks and theMicrotask Queuefor microtasks. - Macrotasks come from external browser APIs (Web API), microtasks are a built in mechanism of the JS engine.
- Exactly one macrotask runs per turn of the Event Loop.
- Microtasks all run in a row until their queue is empty.
- Microtasks have the higher priority:
Promise.thenalways beatssetTimeout(..., 0). - The cycle looks like this: an empty stack, all microtasks, the next macrotask, repeat.
Quick example
javascript
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');Output order:
text
1
4
3
2Comparison table
| Criterion | Macrotasks (Task Queue) | Microtasks (Microtask Queue) |
|---|---|---|
| When they run | After the stack and all microtasks are cleared | Right after the current code, before a macrotask |
| Queue | Task Queue | Microtask Queue |
| Examples | setTimeout, setInterval, setImmediate, event handlers, XHR | Promise.then, Promise.catch, queueMicrotask, MutationObserver |
| Priority | lower | higher |
| How many run at once | One per Event Loop iteration | All microtasks in a row while the queue is not empty |
| Source | External browser APIs (Web API) | Built in microtasks of the JS engine |
| Typical purpose | Deferred execution, asynchronous events | Reacting to settled promises, small chains of actions |
Walking through the example
1and4run synchronously, on the call stack.Promise.then(...)is placed in the microtask queue.setTimeout(...)is placed in the macrotask queue.- Once the stack is cleared, all microtasks run, which prints
3. - Then the first macrotask runs, which prints
2.
Note that the order of the lines in the source does not matter here. Even if setTimeout is declared first, the promise still fires earlier, because its callback sits in a different, higher priority queue.
An important detail: what the Event Loop does after each macrotask
After each macrotask the Event Loop:
- runs all microtasks until their queue is empty,
- then takes the next macrotask,
- and repeats the cycle.
This is why microtasks scheduled from inside a microtask still run in the same turn rather than the next one.
Visually
text
+-----------------------------------------------------+
| Function calls (Call Stack) |
|-----------------------------------------------------|
| Microtasks (Microtask Queue) | <- right after the stack
|-----------------------------------------------------|
| Macrotasks (Task Queue) | <- after the microtasks
+-----------------------------------------------------+Terms at a glance
| What | When | Example |
|---|---|---|
| Microtask | After the current code, before macrotasks | Promise.then, queueMicrotask |
| Macrotask | After all microtasks | setTimeout, setInterval, DOM events |
| Event Loop | Keeps the order: empty stack, microtasks, a macrotask, repeat | the whole loop control |
Common mistakes
- Assuming the order follows the order of lines in the code. It follows the queue the callback landed in.
- Reading
setTimeout(..., 0)as "run right now". It is the earliest possible macrotask, which means after every microtask. - Confusing
fetchitself with itsthen. The network response arrives through a Web API, but thethencallback is already a microtask. - Spawning microtasks endlessly. The queue never drains, so macrotasks and rendering never get control.
- Expecting the same order in the browser and in Node.js. The set of macrotask sources differs, for example
setImmediateexists only in Node.js.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.