Skip to main content

Microtasks in JavaScript

Microtasks are small tasks that run right after the current code (the current macrotask) and before the next macrotask from the queue begins. They form a separate structure, the Microtask Queue.

Theory

TL;DR

  • A microtask is a callback the Event Loop runs after the current code but before any macrotask.
  • The main sources are Promise.then, Promise.catch, queueMicrotask and MutationObserver.
  • The microtask queue is drained completely, while only one task is taken from the macrotask queue per turn.
  • Microtasks have higher priority, so Promise.then always beats setTimeout(..., 0).
  • Spawning microtasks endlessly "freezes" the Event Loop: macrotasks and rendering never get control.
  • The order is: the stack, all microtasks, the next macrotask.

Quick example

javascript
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4');

The resulting output order:

text
1 4 3 2

The key idea

JavaScript has two levels of tasks:

TypeExamplesWhen it runs
Macrotasks (Tasks)setTimeout, setInterval, DOM events, network callbacksAfter the stack is cleared
MicrotasksPromise.then, Promise.catch, queueMicrotask, MutationObserverRight after the current task, before the next macrotask

In other words, after the current code (a function, for example) has finished, the Event Loop first checks the microtask queue. If there is anything in it, all of those tasks run one after another until the queue is empty.

Walking through the example

  1. console.log('1') runs immediately. Output: 1
  2. setTimeout(..., 0): the callback lands in the macrotask queue (task queue).
  3. Promise.then(...): the callback lands in the microtask queue.
  4. console.log('4') runs immediately. Output: 4
  5. The current stack is empty, so all microtasks run, that is console.log('3').
  6. Only after that do macrotasks run, that is console.log('2').

What creates microtasks

SourceExample
PromisePromise.resolve().then(...)
queueMicrotask()queueMicrotask(() => { ... })
MutationObserverWatching DOM changes

async/await belongs here too: the continuation after an await is scheduled as a microtask.

Microtasks vs macrotasks

PropertyMicrotasksMacrotasks
QueueMicrotask QueueTask Queue
ExamplesPromise.then, queueMicrotasksetTimeout, setInterval, click
PriorityHigher, they run earlierLower
When they runAfter the current code, but before the next eventAfter all microtasks

Be careful with microtasks

If you keep adding new microtasks inside then forever, the engine never reaches the next macrotask and you end up with a "frozen" Event Loop:

javascript
function loop() { Promise.resolve().then(loop); } loop(); // the browser will hang

The stack does not overflow here, because every microtask completes properly. The microtask queue simply never becomes empty, so timers, event handlers and page repaints never get their turn.

Terms at a glance

TermDescription
MicrotaskA small task that runs after the current code and before the next macrotask
Microtask queueHolds Promise.then, queueMicrotask and similar callbacks
PriorityHigher than that of macrotasks
Event Loop orderThe stack, then all microtasks, then the next macrotask

Common mistakes

  • Treating Promise.then and setTimeout(..., 0) as equals. A promise is a microtask and will always run first, no matter the order in the code.
  • Thinking a microtask is just a "faster timer". It is a different queue with a different priority, not a smaller delay.
  • Scheduling microtasks recursively for background work. That blocks rendering; use setTimeout or requestIdleCallback for chunked work.
  • Forgetting that the continuation after await is a microtask too. That is why code after await runs later than people expect, yet still before any timer.
  • Confusing the microtask queue with the call stack. Microtasks only run once the stack is already empty.

Short Answer

Interview ready
Premium

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