Skip to main content

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 Queue for macrotasks and the Microtask Queue for 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.then always beats setTimeout(..., 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 2

Comparison table

CriterionMacrotasks (Task Queue)Microtasks (Microtask Queue)
When they runAfter the stack and all microtasks are clearedRight after the current code, before a macrotask
QueueTask QueueMicrotask Queue
ExamplessetTimeout, setInterval, setImmediate, event handlers, XHRPromise.then, Promise.catch, queueMicrotask, MutationObserver
Prioritylowerhigher
How many run at onceOne per Event Loop iterationAll microtasks in a row while the queue is not empty
SourceExternal browser APIs (Web API)Built in microtasks of the JS engine
Typical purposeDeferred execution, asynchronous eventsReacting to settled promises, small chains of actions

Walking through the example

  1. 1 and 4 run synchronously, on the call stack.
  2. Promise.then(...) is placed in the microtask queue.
  3. setTimeout(...) is placed in the macrotask queue.
  4. Once the stack is cleared, all microtasks run, which prints 3.
  5. 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:

  1. runs all microtasks until their queue is empty,
  2. then takes the next macrotask,
  3. 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

WhatWhenExample
MicrotaskAfter the current code, before macrotasksPromise.then, queueMicrotask
MacrotaskAfter all microtaskssetTimeout, setInterval, DOM events
Event LoopKeeps the order: empty stack, microtasks, a macrotask, repeatthe 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 fetch itself with its then. The network response arrives through a Web API, but the then callback 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 setImmediate exists only in Node.js.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.