Microtask Queue
The microtask queue stores small units of deferred work, such as Promise callbacks, that JavaScript executes after the current synchronous code finishes. Microtasks are processed before the event loop moves on to another task, allowing asynchronous operations to run in a predictable order.
JavaScript uses an event loop to coordinate synchronous code and asynchronous operations. The microtask queue is a scheduling mechanism for callbacks that need to run shortly after the current execution finishes. Common sources include Promise handlers registered with `then()`, `catch()`, and `finally()`, as well as `queueMicrotask()`.
When JavaScript starts running a synchronous script, it continues until that execution completes. Once the current task finishes, the runtime performs a microtask checkpoint, during which it processes queued microtasks. This happens before the browser proceeds to another task, such as a timer callback or an input event.
Microtasks are generally processed in the order they are queued. If a microtask schedules another microtask, the newly added work is normally processed during the same checkpoint, provided the queue eventually becomes empty. This behavior can delay other tasks if microtasks continue to be added indefinitely.
Promises are a common source of microtasks. For example, a callback registered with `Promise.resolve().then()` does not execute immediately. Instead, it is scheduled to run after the current synchronous code completes. This explains why a Promise callback can execute before a `setTimeout()` callback, even when the timer uses a delay of zero.
For frontend development, understanding microtasks helps explain the ordering of asynchronous callbacks, UI updates, and event handlers. Browsers generally perform rendering opportunities between tasks and other event-loop processing steps, so a long chain of microtasks can delay visual updates.
Key points
- Microtasks run after the current synchronous execution completes.
- Promise handlers and `queueMicrotask()` callbacks are common microtask sources.
- Microtasks are processed before the event loop proceeds to another task.
- Microtasks queued by other microtasks are generally processed in the same checkpoint.
- An endless chain of microtasks can delay timers, user interactions, and rendering.
Interview questions
What is the microtask queue in JavaScript?
The microtask queue holds deferred callbacks that are ready to run after the current synchronous execution finishes. Promise handlers and callbacks registered through queueMicrotask() are common examples. The runtime processes microtasks at a checkpoint before moving on to another task. This scheduling behavior helps determine the order in which asynchronous callbacks execute.
What is the output of a Promise callback and a zero-delay timer?
If synchronous code schedules a Promise callback and a zero-delay timer, the synchronous statements execute first. After the current task finishes, the Promise callback runs as a microtask. The timer callback runs later as a task when it becomes eligible and the event loop reaches it. Therefore, the Promise callback usually appears before the timer callback in this example.
What happens when a microtask schedules another microtask?
The newly scheduled microtask is added to the queue. Under standard microtask checkpoint behavior, the runtime continues processing queued microtasks until the queue is empty. This means a microtask created by another microtask can run before the next task begins. If microtasks keep scheduling additional microtasks indefinitely, they can prevent other work from progressing.
How does the microtask queue affect browser rendering?
Browsers coordinate JavaScript execution, event processing, and rendering through the event loop. Microtasks are processed at defined checkpoints, and rendering opportunities generally occur between tasks rather than after every individual microtask. A large microtask queue can therefore delay the browser's ability to update the screen. Developers should avoid unnecessarily long chains of microtasks when responsiveness matters.
How does queueMicrotask() differ from Promise.resolve().then()?
Both approaches can schedule work to execute as a microtask. queueMicrotask() directly schedules a callback without creating a Promise chain. Promise.resolve().then() registers a handler on a Promise and returns another Promise, allowing chaining and Promise-based error propagation. The direct microtask API is useful when the goal is simply to defer a callback until the current execution finishes.