DEV Community

Nguyen Dinh Khai
Nguyen Dinh Khai

Posted on

How the Event Loop Handles Microtasks and Macrotasks

In JavaScript, microtasks and macrotasks are two types of asynchronous tasks that the event loop manages, but they are handled in different ways. Understanding how they work is crucial for predicting the execution order of asynchronous code.

1. Macrotask Queue (Task Queue)

  • Macrotasks are placed into their own queue, often referred to as the task queue or macrotask queue.
  • Examples of macrotasks include: setTimeout, setInterval, I/O events, and DOM events (like click and load).
  • The event loop takes each macrotask from this queue to execute, but only after all microtasks in the microtask queue have been processed.

2. Microtask Queue

  • Microtasks have their own queue called the microtask queue.
  • Examples of microtasks include: .then() of Promise, MutationObserver.
  • The microtask queue has a higher priority than the macrotask queue. After each macrotask is executed, the event loop will process all the microtasks in this queue before moving on to the next macrotask.

How the Event Loop Handles Microtasks and Macrotasks:

  1. Event Loop starts a new cycle.
  2. Execute all synchronous code in the call stack.
  3. When the call stack is empty
    • Execute all microtasks in the microtask queue. If a microtask adds more microtasks, they will be executed immediately within the same cycle.
    • Move to the first macrotask in the macrotask queue and execute it.
  4. Repeat this process.

Example:

console.log('Start');

setTimeout(() => {
  console.log('Macrotask 1');

  Promise.resolve().then(() => {
    console.log('Microtask inside Macrotask 1');
  });
}, 0);

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

console.log('End');

Enter fullscreen mode Exit fullscreen mode

Output

Start
End
Microtask 1
Microtask 2
Macrotask 1
Macrotask 2

Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. console.log('Start'): Executes immediately and prints "Start".
  2. setTimeout(..., 0) (Macrotask 1): Added to the macrotask queue.
  3. Promise.resolve().then(...) (Microtask 1): Added to the microtask queue.
  4. setTimeout(..., 0) (Macrotask 2): Added to the macrotask queue.
  5. Promise.resolve().then(...) (Microtask 2): Added to the microtask queue.
  6. console.log('End'): Executes immediately and prints "End".
  7. Microtasks: The event loop will process all microtasks in the microtask queue before processing any macrotasks:
    • Prints "Microtask 1".
    • Prints "Microtask 2".
  8. Macrotasks:
    • Executes the first setTimeout(..., 0) (Macrotask 1), printing "Macrotask 1".
    • Executes the second setTimeout(..., 0) (Macrotask 2), printing "Macrotask 2".

Summary:

  • Microtasks and macrotasks are placed into two separate queues: the microtask queue and the macrotask queue.
  • The microtask queue is processed before the macrotask queue in the event loop.
  • After every macrotask, the event loop processes all microtasks in the microtask queue before moving to the next macrotask in the macrotask queue.
  • This ensures that microtasks (e.g., Promise callbacks) have a higher priority and are executed sooner than macrotasks.

Top comments (0)