When Some Code Never Gets Its Turn: Understanding Starvation in JavaScript
Starvation in JavaScript occurs when certain tasks are continuously delayed or never executed because higher-priority or long-running tasks monopolize the event loop, preventing other queued work from getting a chance to run.
Thereâs a kind of bug that doesnât crash your application.
It doesnât throw errors.
It doesnât break logic.
It simply⌠never happens.
A callback that never runs.
A UI that never updates.
A timer that seems to be ignored.
And the confusing part is this:
Everything looks correct.
The task is scheduled.
The code is valid.
But it never executes.
Not BlockedâBut Starved
At first, this might feel like a blocking issue.
But starvation is different.
Nothing is frozen.
The system is still running.
Itâs just that some tasks never get a chance to run.
Because others keep going first.
The Hidden Competition
JavaScript doesnât run tasks randomly.
It follows a strict order:
- current execution
- microtasks
- macrotasks
And that order is enforced every cycle.
But thereâs a detail that changes everything:
All microtasks must finish before moving on
Not one.
All of them.
When Priority Becomes a Problem
Microtasks are designed to be fast follow-ups.
Promise resolutions.
Async continuations.
They are meant to run immediately after the current code.
But what happens if they never stop?
function loop() {
Promise.resolve().then(loop)
}
loop()Each microtask schedules another.
And the system keeps processing them.
Because it must.
It cannot move on until the queue is empty.
But the queue never empties.
The Starvation Effect
Now imagine thereâs a timer waiting:
setTimeout(() => console.log("Hello"))Itâs ready.
Itâs scheduled.
But it never runs.
Because the system never reaches the point where macrotasks are allowed.
The timer isnât broken.
Itâs starved.
Not Just Microtasks
Starvation can take other forms too.
A long-running loop can block everything:
while (true) {}Here, nothing else runs at all.
No rendering.
No events.
This is not starvation by priorityâ
but by occupation.
The thread is simply never released.
Rendering Can Starve Too
Even when tasks do complete, if each one takes too long,
the browser doesnât get a chance to render.
Frames drop.
Interactions lag.
The system is workingâ
but the user experiences nothing.
Because rendering never gets its turn.
A Question of Fairness
What makes starvation interesting is that JavaScript doesnât enforce fairness.
It enforces order.
If one type of task keeps entering the system at the right priority,
it can dominate.
And everything else must wait.
Breaking the Cycle
The solution is not to avoid asynchronous codeâ
but to respect the systemâs balance.
Break long tasks into smaller pieces.
Avoid infinite scheduling loops.
Allow the event loop to move forward.
Give other tasks a chance to run.
A Different Perspective
Starvation is not a failure of JavaScript.
Itâs a consequence of how it guarantees execution order.
It ensures that certain tasks always run first.
But it does not ensure that everything runs eventually.
That responsibility is left to you.
A Final Thought
In JavaScript, scheduling is not just about when something runs.
Itâs about whether it ever gets the chance.
Because when one part of the system keeps going without pauseâ
another part may quietly wait forever.
And nothing in the language will stop that from happening.