Before Anything Else Runs: Understanding the Microtask Queue in JavaScript
The microtask queue processes high-priority asynchronous tasks like Promise callbacks immediately after the current execution completes and before any macrotasks, ensuring that internal continuations run as soon as possible.
Thereâs a moment in JavaScript that feels almost invisible.
You schedule something to run âlater.â
But then something else runs first.
Not because it was scheduled earlier.
But because it was⌠prioritized.
And that priority lives in a place most people donât see at first:
the microtask queue.
Not All âLaterâ Is the Same
When you write asynchronous code, itâs easy to think in one dimension:
Now vs later.
But JavaScript doesnât work with just one âlater.â
It has layers.
Some tasks wait their turn.
Others insist on being handled immediately after the current execution.
Microtasks belong to the second group.
A Different Kind of Queue
Microtasks are not like timers or user events.
They are not triggered by the outside world.
They are scheduled from within JavaScript itself.
Promises are the most common example:
Promise.resolve().then(() => {
console.log("microtask")
})This doesnât run immediately.
But it doesnât wait like a timer either.
It waits just enough.
The Moment After
Microtasks run at a very specific point:
Right after the current execution finishes
This creates a subtle but powerful rule:
- finish current code
- run all microtasks
- then move on to the next task
And that âallâ matters.
Because microtasks donât run one at a time.
They run until there are none left.
The Feeling of Priority
This is why microtasks often feel like they âjump ahead.â
Even if a timer is scheduled earlier:
setTimeout(() => console.log("macro"))
Promise.resolve().then(() => console.log("micro"))The output will be:
micro
macroBecause microtasks are always processed first.
Not because they were earlierâ
but because they are more urgent.
Why This Exists
JavaScript needed a way to handle certain follow-up work immediately.
Things like:
- resolving promises
- continuing async functions
- reacting to completed operations
These are not external events.
They are internal continuations.
And delaying them too long would break the flow of logic.
So microtasks were given priority.
The Chain Reaction
One of the most interesting properties of microtasks is this:
They can schedule more microtasks.
Promise.resolve().then(() => {
console.log("A")
Promise.resolve().then(() => console.log("B"))
})Output:
A
BThe system keeps processing microtasks until the queue is empty.
It doesnât move on halfway.
It finishes the entire chain.
A Subtle Risk
This behavior can be powerfulâ
but also dangerous.
If microtasks keep scheduling more microtasks endlessly,
the system can become stuck.
Not frozen in the usual senseâ
but unable to move on to other work.
This is called microtask starvation.
Everything else waits.
Where This Shows Up
Youâve already used microtasks, even if you didnât notice:
Promise.thenasync/await- certain framework updates
These rely on microtasks to ensure that updates happen quickly and consistently.
A Different Way to Think About It
Macrotasks are arrivals.
They come from outside:
- user input
- timers
- network events
Microtasks are continuations.
They come from inside:
- resolving a promise
- finishing an async step
They are the system tying up its own loose ends.
A Final Thought
The microtask queue is easy to overlook.
It doesnât introduce new syntax.
It doesnât feel like a separate concept.
But it shapes the order of everything that happens.
Because in JavaScript, not all âlaterâ is equal.
Some things wait their turn.
And some things insist on being finished first.