W3docs

JavaScript Microtasks

In JavaScript, the microtask queue is a critical component of the asynchronous execution model. It prioritizes certain callbacks, ensuring they run after the

In JavaScript, the microtask queue is a critical component of the asynchronous execution model. It prioritizes certain callbacks, ensuring they run after the current script but before the event loop continues with other tasks like rendering or handling user events. This guide offers a deep dive into effectively utilizing the microtask queue to enhance JavaScript applications.

Understanding the Event Loop and Microtask Queue

The JavaScript engine uses an event loop that manages the execution of scripts, allowing for non-blocking operations. The microtask queue is part of this event loop. It is used specifically for promises (check JavaScript: Promises) and other operations like queueMicrotask, ensuring that they are processed at the end of the current run of the JavaScript event loop, before the rendering phase.

There are two distinct queues to keep in mind:

  • The microtask queue holds callbacks from resolved/rejected promises and from queueMicrotask(). After every task, the engine drains this queue completely before doing anything else.
  • The macrotask queue (sometimes called the task queue) holds callbacks from setTimeout, setInterval, I/O, and UI events. Only one macrotask runs per loop iteration.

The key rule: when the current synchronous code finishes, the engine first empties the entire microtask queue, and only then picks up the next macrotask. This is why a promise callback always runs before a setTimeout(…, 0) callback that was scheduled earlier. For the full picture of how these queues interact, see Event Loop: Microtasks and Macrotasks.

Example: Basic Promise


javascript— editable

In this example, the console logs 'Script end' first, followed by 'Promise resolved'. This shows how JavaScript defers promise resolutions to the microtask queue.

How Does the Microtask Queue Work?

The microtask queue executes tasks that are scheduled as microtasks. This includes operations from:

  • Promises
  • Object.observe (deprecated)
  • MutationObserver
  • queueMicrotask() API

Each of these microtasks is processed completely before moving on to the next one or before any rendering or other macrotasks are executed.

Scheduling Microtasks

You can directly schedule microtasks using the queueMicrotask function. This function takes a callback and adds it to the microtask queue. As you see, the queue runs after all the other tasks are done.


javascript— editable

Microtask vs. Macrotask: Why a Promise Beats setTimeout(0)

A common point of confusion is why a promise callback runs before a setTimeout(…, 0) callback, even when the timer is scheduled first. The answer is the rule above: once synchronous code finishes, the engine drains the whole microtask queue before touching the macrotask queue, where setTimeout callbacks live. Even a zero-delay timer has to wait.


javascript— editable

The output is always:

1: synchronous start
2: synchronous end
3: promise (microtask)
4: setTimeout (macrotask)

Lines 1 and 2 run first because synchronous code never yields. Then the microtask queue is emptied (the promise callback), and only after that does the next macrotask — the setTimeout callback — get its turn. Learn more about timers in Scheduling: setTimeout and setInterval.

Ordering Within the Microtask Queue

Microtasks themselves run in the order they were queued (FIFO). Mixing .then() callbacks with queueMicrotask() makes this clear — both feed the same queue, so the result is simply first-in, first-out:


javascript— editable

This logs start, end, promise 1, queueMicrotask, promise 2. Note that a microtask scheduled by another microtask is added to the same queue and runs in the same drain cycle — before any rendering or macrotask. That is exactly what makes runaway microtask loops able to starve the rest of the page.

Practical Applications of Microtasks

Microtasks are especially useful in complex web applications for tasks that require immediate attention after the current script but before the system handles other events or re-renders the UI.

Scenario: Real-time Data Processing

Consider a scenario where real-time data from a server must be processed without interrupting the user experience:


javascript— editable

This example demonstrates fetching data asynchronously, processing it, and scheduling a UI update in the microtask queue.

Efficient Error Handling in Promises

Handling errors effectively in asynchronous code is crucial. Using the microtask queue with promise error handling ensures that errors are dealt with immediately after the promise's resolution logic, but before other unrelated tasks.


javascript— editable

Best Practices for Using the Microtask Queue

  1. Ordering Tasks: Understanding when to use microtasks to ensure correct sequencing of operations.
  2. Avoiding Starvation: Ensure the microtask queue does not continuously get new tasks, preventing macrotasks like UI updates from running.
  3. Debugging: Trace and debug microtask execution to avoid unexpected behaviors in asynchronous code.

Conclusion

Mastering the microtask queue in JavaScript is essential for developing advanced, responsive applications. By effectively leveraging this powerful component of the JavaScript execution model, developers can ensure smoother, non-blocking interactions and an improved user experience. This exploration provides the foundational knowledge and practical skills to utilize the microtask queue effectively in any JavaScript-driven project.

Practice

Practice
What is a Microtask in JavaScript?
What is a Microtask in JavaScript?
Was this page helpful?