Micro-task vs Macro-task in Event Loop 💫

365kim
2 min readMar 23, 2024

--

Photo by Oliver Hale on Unsplash

Ever wondered how JavaScript, being a single-threaded environment, handles asynchronous operations so efficiently?

Take a look at the following code snippet. What will the output be: “Bye” then “Hi,” or “Hi” then “Bye”?

setTimeout(() => console.log("Bye"), 0);

new Promise((resolve) => resolve()).then(() => {
console.log("Hi");
});

To answer this, we need to explore the differences between Macro-tasks and Micro-tasks. But first, let’s get a solid grasp of the Event Loop and how it operates.

Event Loop Basics 💫

The Event Loop is a cornerstone concept in JavaScript, facilitating the execution of tasks in a non-blocking, asynchronous manner, even in a single-threaded environment.

It operates in the following sequence:

  1. Execute Task: Executes the synchronous code on the call stack.
  2. Micro-task Queue: Gives priority to processing all Micro-tasks that were queued during previous phases.
  3. Macro-task Queue: Moves to Macro-tasks next, one at a time.
  4. Repeat: Continuously repeats the cycle, guaranteeing asynchronous operations remain non-blocking.

Now, with this understanding, let’s dive deeper into the difference between Micro-tasks and Macro-tasks.

Micro-task vs Macro-task

Understanding the distinction between Micro-tasks and Macro-tasks is key to grasping how JavaScript manages to stay responsive, handling multiple tasks efficiently.

Micro-task 💨

Micro-tasks are executed immediately after the current script execution and before the Event Loop proceeds to the next task.

Despite the “micro” prefix, these tasks actually have higher priority. They are designed for asynchronous execution of high-priority operations efficiently.

Examples include:

  • Promise callbacks (.then, .catch, .finally)
  • Executions deferred with await in async functions
  • MutationObserver API notifications
  • Calls to queueMicrotask()

Micro-tasks play a vital role in maintaining the integrity of state within your application.

Macro-task 🪨

Macro-tasks encompass larger, often more complex operations. They are processed after Micro-tasks, allowing for a balanced execution flow that maintains both responsiveness and computational efficiency.

Examples include:

  • Timers(setTimeout, setInterval)
  • DOM manipulations
  • I/O operations
  • Network requests
  • Event listener callbacks

The Answer: Hi-Bye

Given our understanding, let’s go back to the first question.

setTimeout(() => console.log("Bye"), 0);

new Promise((resolve) => resolve()).then(() => {
console.log("Hi");
});

When the JavaScript engine processes the call stack, it first encounters the setTimeout (a Macro-task) and queues it for later execution. The Promise (leading to a Micro-task) is executed and queued immediately. Since Micro-tasks are processed before the next Macro-task, "Hi" is logged first, followed by "Bye".

Understanding the interplay between Macro-tasks and Micro-tasks, and their execution order within the JavaScript Event Loop is crucial for web app developers. It not only aids in writing efficient code but also in troubleshooting complex asynchronous operations, ultimately leading to improved performance and user experiences in web applications.

--

--

365kim

Web Front-End Developer who believes Every Day Counts!