Understanding Event Loop with Microtasks and Macrotasks in JavaScript
If you’ve ever seen JavaScript print values in an unexpected order, you’ve probably run into the event loop.
Understanding Event Loop with Microtasks and Macrotasks in JavaScript
If you’ve ever seen JavaScript print values in an unexpected order, you’ve probably run into the event loop.
A common example looks like this:
console.log("start");
setTimeout(() => {
console.log("timeout");
}, 0);
Promise.resolve().then(() => {
console.log("promise");
});
console.log("end");
Many developers expect this output:
start
timeout
promise
end
But the actual output is:
start
end
promise
timeout
Why?
The answer lies in JavaScript’s event loop and the difference between microtasks and macrotasks.
JavaScript Is Single-Threaded
JavaScript executes one thing at a time.
That means if one piece of code is running, another piece has to wait.
However, JavaScript can still handle asynchronous operations like timers, API calls, and promises because it uses queues.
The event loop decides which queue to process next.
What Are Macrotasks?
Macrotasks are regular asynchronous tasks.
These tasks are placed into the macrotask queue and are executed only after:
- The current synchronous code is complete
- All pending microtasks are complete
Common examples of macrotasks include:
setTimeoutsetInterval- DOM events like clicks
setImmediatein some environmentsMessageChannelcallbacks
Example:
setTimeout(() => {
console.log("macrotask");
}, 0);
Even with a delay of 0, the callback does not run immediately.
It still waits for the current code and microtasks to finish.
What Are Microtasks?
Microtasks are smaller, higher-priority tasks.
They are executed immediately after the current synchronous code finishes, before the event loop picks the next macrotask.
Common examples of microtasks include:
Promise.then()Promise.catch()Promise.finally()queueMicrotask()MutationObserver
Example:
Promise.resolve().then(() => {
console.log("microtask");
});
Because promises use the microtask queue, they run before setTimeout callbacks.
Understanding the Execution Order
Let’s revisit the original example:
console.log("start");
setTimeout(() => {
console.log("timeout");
}, 0);
Promise.resolve().then(() => {
console.log("promise");
});
console.log("end");
Execution happens in this order:
console.log("start")runssetTimeoutcallback is added to the macrotask queue- Promise callback is added to the microtask queue
console.log("end")runs- Synchronous code finishes
- All microtasks run
- Then the first macrotask runs
That is why the output becomes:
start
end
promise
timeout
Easy Rule to Remember
A simple rule is:
Synchronous code first, then microtasks, then macrotasks.
Why This Matters
Understanding microtasks and macrotasks helps you:
- Debug asynchronous code faster
- Avoid confusing execution order bugs
- Perform better in interviews
- Understand how promises really work
- Write more predictable JavaScript
This concept becomes even more important when working with frameworks like Angular, React, or Node.js, where promises, timers, and async behavior appear everywhere.
What Is the Event Loop?
The event loop is the mechanism in JavaScript that decides what code should run next.
JavaScript is single-threaded, which means it can only execute one thing at a time. The event loop makes asynchronous behavior possible by continuously checking whether the call stack is empty and whether there are pending tasks waiting in queues.
The process generally looks like this:
- Run all synchronous code
- Run all pending microtasks
- Run the next macrotask
- Repeat
A simple mental model is:
Call Stack → Microtask Queue → Macrotask Queue
The event loop keeps repeating this cycle for as long as the application is running.
What Is MessageChannel?
MessageChannel is a browser API that creates two connected ports that can send messages to each other.
Callbacks triggered by MessageChannel are treated as macrotasks, similar to setTimeout.
Example:
const channel = new MessageChannel();
channel.port1.onmessage = () => {
console.log("MessageChannel callback");
};
console.log("start");
channel.port2.postMessage("hello");
console.log("end");
Output:
start
end
MessageChannel callback
Why?
startruns immediatelypostMessage()schedules a macrotaskendruns immediately- Then the event loop processes the
onmessagecallback
MessageChannel is often used internally by libraries and frameworks because it can schedule tasks faster and more consistently than setTimeout(..., 0).
For example, older versions of React used MessageChannel internally for scheduling work.
What Is MutationObserver?
MutationObserver is a browser API that watches for changes in the DOM.
It can detect things like:
- Elements being added or removed
- Attribute changes
- Text changes inside elements
Its callback is placed in the microtask queue, so it runs before macrotasks like setTimeout.
Example:
const div = document.createElement("div");
const observer = new MutationObserver(() => {
console.log("DOM changed");
});
observer.observe(div, {
attributes: true
});
div.setAttribute("class", "active");
console.log("end");
Output:
end
DOM changed
Why?
setAttribute()changes the DOM immediately- The
MutationObservercallback is scheduled as a microtask endlogs first because it is still part of the current synchronous code- After synchronous code finishes, the microtask queue runs
MutationObserver is commonly used for:
- Watching dynamic UI updates
- Detecting when elements are inserted into the page
- Integrating with third-party scripts
- Building browser extensions
- Tracking changes in large applications like Angular or React
Final Thoughts
The event loop can feel confusing at first, but once you understand the queue priority, the behavior becomes much easier to predict.
Remember:
- Synchronous code runs first
- Then microtasks run
- Then macrotasks run
Once you understand that rule, most JavaScript async questions become much easier to solve.
메타데이터
- post_id
- fbb5cc8ee6d8
- slug
- understanding-event-loop-with-microtasks-and-macrotasks-in-javascript-fbb5cc8ee6d8
- url
- https://medium.com/@nikhilbaxi3/understanding-event-loop-with-microtasks-and-macrotasks-in-javascript-fbb5cc8ee6d8
- canonical_url
- https://medium.com/@nikhilbaxi3/understanding-event-loop-with-microtasks-and-macrotasks-in-javascript-fbb5cc8ee6d8
- author_url
- https://medium.com/@nikhilbaxi3
- status
- ok
- fetched_at
- 2026-09-05 20:55:03