Understanding queueMicrotask in JavaScript

Learn how to schedule code execution at the optimal moment in the JavaScript event loop with microtasks

What Are Microtasks?

Microtasks are short functions that execute after the current task completes and only when the JavaScript execution stack is empty, but before the browser returns control to the event loop. Unlike regular tasks (macrotasks) such as those scheduled by setTimeout(), microtasks have higher priority and are processed immediately after the current synchronous execution context finishes.

The microtask queue is processed multiple times per iteration of the event loop, including after handling events and callbacks. When a microtask adds more microtasks by calling queueMicrotask(), those newly-added microtasks execute before the next task runs. This behavior makes microtasks ideal for operations that need to happen "just after" the current work completes.

Understanding microtasks is essential for building responsive user interfaces that execute code predictably and avoid timing-related bugs.

Basic queueMicrotask Example
1console.log('Start');2 3queueMicrotask(() => {4 console.log('Microtask executing');5});6 7console.log('End');8 9// Output:10// Start11// End12// Microtask executing

Tasks vs. Microtasks: Key Differences

Understanding the distinction between tasks and microtasks is essential for writing predictable JavaScript code.

Tasks (Macrotasks)

Tasks are scheduled by:

  • Initial JavaScript program execution
  • Event callbacks (click, submit, etc.)
  • setTimeout() and setInterval()
  • I/O operations
  • Rendering steps

Tasks run one at a time, in the order they were enqueued. After each task completes, the event loop checks for microtasks before moving to the next task.

Microtasks

Microtasks are scheduled by:

  • queueMicrotask() calls
  • Promise .then(), .catch(), .finally() callbacks
  • MutationObserver callbacks
  • Async/await operations (which use promises internally)

The key difference: Each time a task exits, the event loop runs ALL microtasks until the queue is empty, regardless of how many microtasks were added during processing. This fundamental distinction affects everything from CSS animations timing to React state updates.

Tasks vs Microtasks Comparison
CharacteristicTasks (Macrotasks)Microtasks
Scheduled bysetTimeout, setInterval, eventsqueueMicrotask, Promise.then
Execution orderFIFO, one at a timeAll at once until queue empty
PriorityLowerHigher
When runsWhen event loop reaches themAfter sync code, before next task
ExamplessetTimeout, fetch, click handlersPromise callbacks, queueMicrotask

Practical Use Cases

Ensuring Consistent Ordering

One common use case is ensuring consistent execution ordering when working with both synchronous and asynchronous code paths:

// With queueMicrotask - consistent ordering
function processData(data) {
 if (data.cached) {
 return new Promise(resolve => {
 queueMicrotask(() => {
 resolve(data.value);
 });
 });
 } else {
 return fetchData(data.id).then(result => result.value);
 }
}

Batching Operations

Use queueMicrotask() to batch multiple operations:

const pendingOperations = [];

function queueOperation(operation) {
 pendingOperations.push(operation);
 
 if (pendingOperations.length === 1) {
 queueMicrotask(processBatch);
 }
}

function processBatch() {
 const operations = [...pendingOperations];
 pendingOperations.length = 0;
 operations.forEach(op => op());
}

Cleanup After Synchronous Code

Microtasks are ideal for cleanup operations:

function updateComponent() {
 this.state = newState;
 
 queueMicrotask(() => {
 this.cleanup();
 this.triggerAnalytics();
 });
}

For landing page optimization, batching operations with queueMicrotask can significantly improve performance by reducing layout thrashing and unnecessary reflows.

React's Use of queueMicrotask
1// React warns about unawaited act() calls using queueMicrotask2queueSeveralMicrotasks(() => {3 if (!didAwaitActCall && !didWarnNoAwaitAct) {4 console.error(5 'You called act(async () => ...) without await. '6 );7 }8});

Best Practices

When to Use queueMicrotask

Use queueMicrotask() when you need to:

  • Ensure code runs after the current synchronous code completes
  • Maintain consistent ordering between sync and async code paths
  • Defer work until after the current call stack clears
  • Schedule cleanup or analytics after a UI update

For teams implementing complex user interfaces, our web development team can help you integrate microtask patterns into your production applications.

When to Use Alternatives

Consider other approaches in these scenarios:

  • setTimeout(): When you explicitly want to yield to the event loop
  • requestAnimationFrame(): For code that should run before the next paint
  • Promise.resolve().then(): For promise chaining

Performance Considerations

Microtasks execute synchronously until the queue is empty. Recursively adding microtasks can cause an infinite loop:

// WARNING: This creates an infinite loop
queueMicrotask(() => {
 queueMicrotask(() => { /* ... */ });
});

Be cautious about the potential for endless microtask processing.

Error Handling

Errors thrown in queueMicrotask() callbacks are reported as standard exceptions rather than as rejected promises:

queueMicrotask(() => {
 try {
 riskyOperation();
 } catch (error) {
 handleError(error);
 }
});

Proper error handling in microtasks is crucial for maintaining robust website user testing tools and ensuring consistent application behavior.

Summary

The queueMicrotask() API provides a standardized way to schedule code execution at a precise point in the JavaScript event loop. By running microtasks after the current synchronous code completes but before rendering and macrotasks, you gain fine-grained control over execution timing.

Key takeaways:

  • Microtasks execute with higher priority than regular tasks
  • Use queueMicrotask() for predictable deferred execution
  • Avoid infinite microtask recursion
  • Prefer queueMicrotask() over Promise.resolve().then() for non-promise operations

Understanding and leveraging microtasks helps you build more predictable and performant JavaScript applications, especially when implementing landing page templates and other interactive UI components.

Ready to Optimize Your JavaScript Applications?

Our team of experienced developers can help you implement best practices for asynchronous JavaScript, performance optimization, and modern UI development.