In this section, we will learn what the Microtask Queue is and how to use it in JavaScript.
Note: Before reading this section, we’re assuming you’re already familiar with the Promise and its handlers.
Also in this section, it’s important to be familiar with the Stack memory as well as the setTimeout function. So if you’re not familiar with these two topics, please read the related sections first.
What is Microtask Queue in JavaScript?
The microtask queue is a place where the handlers of Promise-objects will be queued in there when those Promise-objects are settled to either resolve or rejection. These handlers in the queue will be executed by the execution engine only when the Stack is empty.
Example: JavaScript microtask queue and Promises
console.log("Hello"); const prom = new Promise((resolve, reject)=>{ setTimeout(()=>{ resolve("Resolved after 3 seconds"); },3000); }); prom.then(resolve=>{ console.log("The first resolve handler"); console.log(resolve); return "RS1"; }).then(resolve=>{ console.log("The second resolve handler"); console.log(resolve); }); console.log("Good Bye");
Output:
Hello Good Bye The first resolve handler Resolved after 3 seconds The second resolve handler RS1
How Does Microtask Queue Work?
Here the execution engine will first run the `console.log(“Hello”);` statement and sends the value of this statement to the browser’s console.
After that it will create a Promise object. Inside the executor function of the Promise, the engine will send a request to the setTimout API to run the function that we set as its argument after 3 seconds. So the timer after 3 seconds will set the function in the queue for the execution engine to invoke and run it.
Moving on to the next statement, the execution engine will reach to the handlers of the `prom` Promise object. Here, because the target Promise is not yet settled, the execution engine will jump over the handlers and run the instructions afterwards.
Note: The execution engine only cares about the handlers of a Promise object when that promise is settled to either resolve or rejection. But here our promise-object is still in the pending state and so the execution engine skips its handlers for now.
This means the engine will move into the `console.log(“Good Bye”);` statement and sends the value of this function to the browser’s console.
After that, when there’s no other task in the Stack to be executed, the engine will take a look at two queues in the same order:
- Microtask queue: This queue is the place where the handlers of the promise-objects are registered in. (You’ll see how the engine will register handlers in just a moment).
- Task queue: This is the queue that external APIs send their request for the execution engine to run them.
Because currently there’s no task registered in the `Microtask queue`, the engine moves to the other queue, which is the `Task queue`.
Let’s say 3 seconds went by and the `setTimout` sends the request in the queue (the request for the engine is to run the function that we set as the argument of the `setTimeout()`).
So the engine will invoke this function and run its body. In the body of this function there’s a call to the `resolve` function of the Promise object `prom`.
This means the target Promise-object is now in the resolve state. So then the execution engine will move on to the next instruction, which in this case is the handlers of the Promise object.
Here, the important thing to know is that the execution engine will only register the first handler of the target Promise in the microtask queue. It won’t get into the body of this handler yet.
After registering the handler, the engine moves on to see if there’s another instruction in the stack to execute. But at this time the stack is empty!
Now the engine checks the microtask queue and invokes the handler of the Promise-object.
It will then move into the body of this handler and run the instructions that are there.
Currently, there are two tasks here:
console.log("The first resolve handler"); console.log(resolve);
So it will run both tasks and send their messages to the browser’s console.
After that it will reach to the return statement of this handler:
return "RS1";
As mentioned before, the `then` method always returns a promise object.
The important thing to remember is that, if the returned promise is already settled (resolved or reject), the engine will register the handler of that promise (in this case the next `then` method) in the microtask queue and immediately invokes and executes the body of that handler.
For this reason, the second `then` handler is immediately executed after the first `then` handler.
That’s how the two messages in the body of the second handler appeared on the browser’s console.
Note: the execution engine starts with the microtask queue and when all the tasks in this queue are executed, it will move to the Task queue to see if there’s a request to be run. Also, the execution in the Task queue is one at a time. This means the engine runs one request from this queue and then it moves to the microtask queue again to see if there’re tasks to be run. Until there’s a task in the `Microtask queue`, no other request in the `Task queue` will be run.