My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more
What are macro and micro-tasks in JavaScript?

What are macro and micro-tasks in JavaScript?

Rupam Das's photo
Rupam Das
·Jun 15, 2021·

5 min read

Before jumping onto macro and micro-tasks, let's quickly see how the event loop works.

Event loop

An Event Loop in JavaScript is a constantly running process that keeps a tab on the call stack.

  1. Checks if the call stack is empty
  2. Takes a callback(or task) from the task queue and puts it inside the call stack
  3. The callback gets executed and pops out of the call stack, and the call stack is empty now.

This loop is continued till the task queue is empty.

Tasks inside the task queue can be broadly classified into 2 categories, micro-tasks, and macro tasks

In this, we will first cover theoretical details on how things work and then move to code examples.

Micro Task

A microtask is a short function that is executed after the function or program which created it exits and only if the JavaScript execution stack is empty, but before returning control to the event loop being used by the user agent to drive the script's execution environment.

One thing to remember, the micro-task runs before the user agent has the opportunity to react to actions taken by the microtasks. Just remember this line, we will cover this in more detail later in the blog.

Examples: Promises, queueMicrotask, MutationObserver

Macro Task

Macro-task represents some discrete and independent work. These are executed always after the execution of the JavaScript code and the micro-task queue is empty.

Examples: setTimeout, setInterval, setImmediate, requestAnimationFrame

If the task queue is not empty and the call stack is empty, the event loop will give priority to the microtasks over macrotasks. If only all microtasks are done, the macro tasks are run.

In the meanwhile, if more microtasks are being added to the task queue, these additional microtasks are also added to the end of the micro-queue and are processed before any macro tasks. This is because event-loop will continue to call all microtasks until there are no microtasks remaining in the task queue.

That was the theory, now let's predict outputs based on our knowledge.

console.log(1);

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

Promise.resolve().then(() => console.log(3));

console.log(4);

output will be

1
4
3
2

If you are correct then, Bravo! you have understood the topic well.

But if you are wrong, let's see, how the piece of code is executed under the hood line by line.

Line 1 - console.log() is synchronous, so it moves to call stack and executed immediately. Prints 1

Line2 - setTimeout() is asynchronous, so it moves to the browser api. Browser api puts a timer on the callback which is the second argument of the setTimeout. As the timer is zero here, it moves to the task queue(if the timer was supposed 2000ms, then it will wait for 2000ms in the browser api environment and then moves to the task queue). This is a macro-task.

Line3 - Promise is asynchronous, so its callback function moves to the task queue. This is a micro-task.

Line4 - console.log() is synchronous, so it moves to call stack and executed immediately. Prints 4

Now the call stack is empty

As microtasks are given priority over macrotasks. So callback of promise moves to the call stack and executes first and Prints 3 in the console, even though macrotask is present before microtask in the task queue.

Now callback of setTimeout will move to call stack and executed. Prints 2

Just remember synchronous task > micro task > macro task

Now let's discuss the point which I told you to remember at the top.

The micro-task runs before the user agent has the opportunity to react to actions taken by the microtasks

While the execution of microtasks the control is not with the browser. This lets the given function run without the risk of interfering with another script's execution.

Now suppose the microtask takes too long to execute, the browser cannot process other tasks like user events(button click, scroll). So after a time, it raises an alert "Page Unresponsive". That happens when there are a lot of complex calculations or a programming error leading to an infinite loop.

Let me give you an example.

Let's take a function that counts from 1 to 1000000000

let i = 0;

let start = Date.now();

function count() {

  // do a heavy job
  for (let j = 0; j < 1e9; j++) {
    i++;
  }

  alert("Done in " + (Date.now() - start) + 'ms');
}

count();

If you run the code below, the engine will “hang” for some time. Try to click other buttons on the page – you’ll see that no other events get handled until the counting finishes.

Now let's split the counting using setTimeout()

let i = 0;

let start = Date.now();

function count() {

  // do a piece of the heavy job (*)
  do {
    i++;
  } while (i % 1e6 != 0);

  if (i == 1e9) {
    alert("Done in " + (Date.now() - start) + 'ms');
  } else {
    setTimeout(count); // schedule the new call (**)
  }

}

count();

Now the browser interface is fully functional during the “counting” process.

For every 1000000 counts code splits

1st run counts - 1 to 1000000

2nd run counts - 1000001 to 2000000

and so on

So when a new event appears when the engine is busy counting 1st run it gets queued and then gets executed when 1st run is done and before the 2nd run starts.

So here is a performance tip, if you have a big sync calculation to do and you can’t use a web worker, consider splitting the task into multiple tasks using setTimeout. That way browser will not become unresponsive!

That's it!!!😍 I hope this blog gives you a basic understanding of how macro and micro-tasks works.