Microtasks and Macrotasks: The Secret to Mastering the JavaScript Event Loop

JavaScript runs on a single thread — so why is it so good at handling async work? The answer is the Event Loop. But to truly understand why one line runs before another, you need to know the difference between Microtasks and Macrotasks — two queues with completely different priorities.

microtaskmacrotaskevent loopJavaScript
Cover image: Microtasks and Macrotasks: The Secret to Mastering the JavaScript Event Loop
Avatar of Trung Vũ Hoàng

Trung Vũ Hoàng

Author

31/3/20264 min read

1. How Does the Event Loop Work?

JavaScript executes code in the Call Stack. When it encounters async work (setTimeout, fetch...), it offloads it and continues running synchronous code. When async operations finish, their results aren’t pushed straight into the Call Stack — they’re placed into a queue. The Event Loop checks: if the Call Stack is empty, it pulls a task from the queue and executes it.

The key point is that there are two types of queues with completely different priorities: the Microtask Queue and the Macrotask Queue.

2. Microtask — Highest Priority

A Microtask is a small task that runs immediately after the Call Stack becomes empty — before any Macrotasks are processed. The Event Loop will drain the entire Microtask Queue before moving on.

Sources of Microtasks:

  • Promise callbacks: .then(), .catch(), .finally()

  • queueMicrotask() — pushes directly into the microtask queue

  • MutationObserver — watches DOM changes

Warning: If a Microtask creates new Microtasks, those will also run immediately. Creating an infinite Microtask loop will block the Event Loop, preventing the browser from rendering UI or handling user input.

3. Macrotask — Patiently Waiting in Line

Macrotasks (or “Tasks”) are larger system-level tasks. The Event Loop pulls only one Macrotask per tick — then it must drain the Microtask Queue before pulling the next Macrotask.

Sources of Macrotasks:

  • setTimeout() / setInterval()

  • setImmediate() (Node.js)

  • I/O events: mouse clicks, key presses, incoming network data

  • Rendering: the browser re-paints the UI

4. Execution Order — The Golden Rule

  1. Run all synchronous code in the Call Stack.

  2. Drain the entire Microtask Queue.

  3. Execute one Macrotask.

  4. Go back to step 2 (check for new Microtasks).

  5. Repeat.

5. The “Legendary” Example That Shows Up in Every Interview

console.log('1 - Synchronous');

setTimeout(() => {
  console.log('2 - Macrotask');
}, 0);

Promise.resolve().then(() => {
  console.log('3 - Microtask (Promise)');
});

queueMicrotask(() => {
  console.log('4 - Microtask (Explicit)');
});

console.log('5 - Final synchronous');

Output:

1 - Synchronous
5 - Final synchronous
3 - Microtask (Promise)
4 - Microtask (Explicit)
2 - Macrotask

If you understand why 5 prints before 3, and why 3 prints before 2 — you already understand 80% of how the Event Loop works.

6. Real-World Use Cases and Performance Notes

Split heavy work so the UI doesn’t freeze

If a computation takes 1 second, don’t put it in a Promise. Use setTimeout to chunk it — allowing the browser to interleave rendering between tasks:

function processChunk(data, index) {
  if (index >= data.length) return;
  // Process one part
  processItem(data[index]);
  // Yield to the browser to render, then continue
  setTimeout(() => processChunk(data, index + 1), 0);
}

Guarantee ordering after state changes

Use queueMicrotask() when you need an action to run right after a state update but before any Macrotask runs — useful in reactive libraries.

Avoid Microtask Hell

Recursion inside a Promise can block the Macrotask Queue forever — your app won’t be able to respond to user clicks or scrolling anymore.

Conclusion

Understanding Microtasks and Macrotasks is the step from coder to a real JavaScript engineer:

  • Microtask → runs immediately after each synchronous task, with absolute priority.

  • Macrotask → waits in line, only one task per Event Loop tick.

  • Order: Sync → Microtask → Macrotask → Microtask → Macrotask → ...

This knowledge is foundational for debugging timing issues in React, Node.js, and any JavaScript framework.

Frequently Asked Questions

Share this article
Zalo

Found this article helpful?

Contact us for a free consultation about our services

Contact us

Bài viết liên quan