When âSoonâ Never Comes: Understanding Microtask Starvation in JavaScript
Microtask starvation occurs when microtasks continuously schedule more microtasks, preventing the event loop from progressing to macrotasks or rendering, causing other operations to be indefinitely delayed.
Thereâs a promise in JavaScript that feels reassuring.
You schedule something, and you expect it to run.
Maybe not immediatelyâbut soon.
And most of the time, thatâs true.
Until one day, something quietly breaks that expectation.
A timer never fires.
The UI stops updating.
The system feels stuckâ
even though nothing has crashed.
The Invisible Priority
JavaScript doesnât treat all asynchronous work equally.
Some tasks are allowed to wait.
Others are not.
Microtasks belong to the second group.
They are designed to run immediately after the current execution finishes.
Not later.
Not eventually.
But as soon as possible.
A Rule That Changes Everything
After any piece of JavaScript completes, the engine does something very specific:
It processes all microtasks before moving on
Not one microtask.
Not a few.
All of them.
And this is where things begin to shift.
A Loop That Never Ends
Consider this:
function loop() {
Promise.resolve().then(loop)
}
loop()Each microtask schedules another.
And because the system must finish all microtasks before continuingâ
it never leaves the microtask phase.
The queue is never empty.
And so the system never moves forward.
What Gets Left Behind
Now imagine thereâs a timer:
setTimeout(() => console.log("Hello"))Itâs ready.
Itâs waiting in the macrotask queue.
But it never runs.
Because the system never reaches it.
The microtasks keep coming.
And they always go first.
Not a BugâBut a Consequence
This behavior is not accidental.
Microtasks were designed to handle immediate follow-up work:
- resolving promises
- continuing async operations
- finalizing internal steps
They are meant to complete quickly.
But when they donâtâ
when they keep scheduling more workâ
the system has no way to interrupt them.
The Starvation Effect
This creates what is known as microtask starvation.
Not because the system is frozen.
But because one kind of work dominates completely.
Everything else waits.
Indefinitely.
A System Without Fairness
JavaScript guarantees order.
But it does not guarantee fairness.
If microtasks keep arriving, they will always be processed first.
There is no mechanism that says:
âEnough. Let something else run.â
And so the responsibility shifts to you.
Breaking the Cycle
Avoid patterns that endlessly schedule microtasks.
Be cautious with recursive promise chains.
And remember that even something that feels lightweightâ
can become overwhelming when repeated without pause.
Sometimes, the solution is simply to step back.
To allow the system to breathe.
A Subtle Lesson
Microtask starvation reveals something deeper about JavaScript.
Itâs not just about what runs.
Itâs about when the system is allowed to move on.
And if you prevent that transitionâ
intentionally or notâ
you can create a system that is always busy,
but never progressing.
A Final Thought
In JavaScript, âsoonâ has a very specific meaning.
It means:
âBefore anything else happensâ
And if that âanything elseâ never gets its turn,
then âsoonâ quietly becomesâŚ
never.