JavaScript tutorials > Advanced Concepts > Asynchronous JavaScript > What are microtasks and macrotasks in JavaScript?
What are microtasks and macrotasks in JavaScript?
Understanding microtasks and macrotasks is crucial for writing efficient and predictable asynchronous JavaScript code. They are the building blocks of the event loop, which manages the execution of tasks in a non-blocking manner. This tutorial will explain the concepts with clear examples.
The JavaScript Event Loop
JavaScript is single-threaded, meaning it can only execute one operation at a time. The event loop is what allows JavaScript to perform non-blocking operations, like handling user events, fetching data from a server, or setting timers, without freezing the user interface. The event loop continuously monitors the call stack and the task queues (macrotask and microtask queues). If the call stack is empty, it picks a task from one of the queues and pushes it onto the call stack for execution.
Macrotasks (Task Queue)
Macrotasks represent larger, discrete units of work. They are added to the macrotask queue. Examples of macrotasks include: The event loop processes one macrotask at a time, from the oldest to the newest. After processing a macrotask, the event loop will then check the microtask queue.setTimeout
setInterval
setImmediate
(Node.js)
Microtasks (Microtask Queue)
Microtasks are smaller, more immediate tasks that need to be executed as soon as possible after the current macrotask completes and before the browser has a chance to re-render or handle other events. Microtasks are added to the microtask queue. Examples of microtasks include: Crucially, the microtask queue is processed completely after each macrotask. This means all pending microtasks will be executed before the event loop moves on to the next macrotask. If new microtasks are added while the queue is being processed, they will also be executed before the next macrotask..then()
, .catch()
, .finally()
)queueMicrotask()
process.nextTick()
(Node.js)
Code Example: Macrotasks vs. Microtasks
Here's a code example that demonstrates the difference between macrotasks and microtasks: Output: Explanation:Script start
Script end
queueMicrotask
Promise.then
setTimeout
setTimeout
callback is scheduled (macrotask) and added to the macrotask queue..then()
callback is scheduled (microtask) and added to the microtask queue.queueMicrotask()
callback is added to the microtask queue.queueMicrotask
is executed, logging 'queueMicrotask'.Promise.then
is executed, logging 'Promise.then'.setTimeout
callback.setTimeout
callback is executed, logging 'setTimeout'.
console.log('Script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('Promise.then');
});
queueMicrotask(() => {
console.log('queueMicrotask');
});
console.log('Script end');
Concepts Behind the Snippet
The code demonstrates the priority given to microtasks over macrotasks. Even though the setTimeout
has a delay of 0 milliseconds, the microtasks from the Promise and queueMicrotask
are executed first. This is because microtasks are processed immediately after the current macrotask finishes and before the event loop moves on to the next macrotask.
Real-Life Use Case: UI Updates
Imagine you have a button that, when clicked, needs to update the UI with some data fetched from an API. You might use Promises to handle the asynchronous API call. The .then()
callbacks, which handle the UI updates, will be executed as microtasks. This ensures that the UI is updated immediately after the data is received and before the browser repaints, providing a smoother user experience.
Best Practices
queueMicrotask
for scheduling microtasks: It is the standard API now, better than relying on promise implementation details.
Interview Tip
During interviews, be prepared to explain the event loop, the difference between macrotasks and microtasks, and provide examples of each. You should also be able to trace the execution order of asynchronous code snippets.
When to Use Them
Memory Footprint
Microtasks generally have a smaller memory footprint compared to macrotasks because they are designed to be short-lived and executed quickly. Macrotasks, especially those involving I/O operations or timers, might hold onto resources longer, contributing to a larger memory footprint until they are completed.
Alternatives
While microtasks and macrotasks are fundamental to asynchronous JavaScript, alternative patterns and libraries can help manage asynchronous operations more effectively:
Pros
Microtasks: Macrotasks:
Cons
Microtasks: Macrotasks:
FAQ
-
What happens if a microtask adds another microtask?
If a microtask adds another microtask to the queue, the new microtask will also be executed during the same microtask processing phase, before the event loop moves on to the next macrotask. This continues until the microtask queue is empty. -
Can I use microtasks to replace macrotasks?
No. Microtasks are designed for short, immediate tasks that must be executed before the browser has a chance to repaint or handle other events. Macrotasks are for larger, more discrete units of work that can be deferred without impacting the immediate responsiveness of the application. Using microtasks to replace macrotasks can lead to performance issues. -
How do web workers relate to macrotasks and microtasks?
Web Workers run in a completely separate thread from the main thread, allowing them to perform long-running tasks without blocking the UI. While web workers have their own event loop, they communicate with the main thread via message passing. The receipt and processing of messages sent from a web worker to the main thread occurs as a macrotask.