Glossary / Fairness and starvation
Fairness and starvation
Fairness describes whether runnable or waiting operations receive a reasonable opportunity to make progress; starvation occurs when one operation waits indefinitely while others continue. JavaScript does not provide a universal fairness guarantee across timers, tasks, microtasks, workers, or host APIs, so application scheduling must avoid monopolizing queues and resources.
Event-loop fairness
A JavaScript callback generally runs to completion. A long synchronous callback blocks every other callback on that event loop. Repeatedly enqueueing microtasks can also prevent the host from reaching another task or rendering opportunity:
function monopolize() {
queueMicrotask(monopolize);
}
// Calling monopolize() can prevent task-level work from progressing.
In Node.js, recursive process.nextTick() scheduling can similarly delay I/O and other phases. Browser rendering opportunities, task selection, timer clamping, and background-tab throttling are host policies; do not infer a portable priority order from API names alone.
Fairness also applies to application-level pools and queues. A FIFO queue can give arrival order, but one tenant may consume all capacity; a priority queue may starve low-priority tasks. A semaphore can be unfair if waiters race to acquire permits. Retries can amplify contention and repeatedly displace new work.
Improving progress guarantees
Keep callbacks short and divide CPU work into bounded chunks, yielding with a mechanism appropriate to the host (for example, a task-level yield rather than an endless microtask chain). Use worker threads or Web Workers for substantial CPU-bound work. For shared pools, consider per-tenant quotas, weighted fair queuing, aging priorities, and bounded queues. Set deadlines and cancellation for work that no longer has value, and reserve capacity for control-plane operations such as shutdown or health checks.
Measure tail wait time, not only average throughput. Track queue age, event-loop delay, worker utilization, and per-priority completion. A timeout detects lack of progress but does not create fairness; cancellation and recovery need explicit semantics.
Interview questions
- Does JavaScript guarantee that every queued callback runs soon? No; hosts define scheduling and work can monopolize an event loop or resource pool.
- How can microtasks starve timers? The host drains queued microtasks before advancing to another task, and each microtask can enqueue more.
- FIFO queue vs fairness? FIFO is one ordering policy, but it does not ensure fair resource shares among tenants or priorities.
- How can priority scheduling starve low-priority work? Continuous high-priority arrivals may always be selected first; aging or reserved capacity can help.
- What should fairness metrics include? Queue age and tail wait latency by class or tenant, along with throughput and event-loop delay.