- Jh123x: Blog, Code, Fun and everything in between./
- My Blog Posts and Stories/
- JavaScript: The Event Loop and Micro/Macro Task Queues/
JavaScript: The Event Loop and Micro/Macro Task Queues
Table of Contents
Introduction #
JavaScript is a single-threaded language, which means it can only execute one task at a time. However, JavaScript has a mechanism called the event loop that allows it to handle asynchronous operations.
In this article, we will discuss how the event loop works and how JavaScript manages the execution of asynchronous tasks using the micro task and macro task queues.
The Event Loop #
The event loop is a mechanism that allows JavaScript to execute async
tasks in a non-blocking manner on a single thread.
It is implemented in the JavaScript runtime environment (e.g., the browser or Node.js) and is responsible for managing the execution of tasks in the call stack and the task queues.
The 2 Queues #
The event loop consists of 2 queues:
- The Micro task queue
- The Macro task queue (Or Task Queue)
The Micro Task Queue #
This is the higher priority queue and is used to schedule tasks that need to be executed after the current task has completed.
The following are examples are scheduled in the micro task queue:
process.nextTick
functionsPromise
resolve/reject callbacksqueueMicrotask
functionsMutationObserver
callbacks
The Macro Task Queue #
This is the lower priority queue and is used to schedule tasks that need to be executed after the current task has completed.
The following examples are scheduled in the macro task queue:
setTimeout
function callssetInterval
function callssetImmediate
function callsrequestAnimationFrame
function calls- I/O operations
- UI rendering
How the queues are processed #
If the call stack is not empty, the javascript runtime will continue to execute the code on the call stack.
When the call stack is empty, the event loop will check the micro task queue first.
If there are tasks in the micro task queue, the event loop will execute them all before checking the macro task queue.
After all the tasks in the micro task queue are executed, the event loop will check the macro task queue.
After the 1st macro task is executed, the event loop will check the micro task queue again before executing the next macro task.
An Example #
Let us start off with an example. Consider the following code snippet:
function recursion(n) {
if (n === 0) {
return;
}
alert(n);
recursion(n - 1);
}
setTimeout(() => alert("Timeout"), 0);
Promise.resolve().then(() => alert("Promise"));
recursion(2);
alert("End");
If you click the “Run” button, you will see three alert boxes displayed in the following order:
- 2
- 1
- End
- Promise
- Timeout
Let us walk through the code step by step
Line 1: setTimeout(() => alert("Timeout"), 0);
#
setTimeout
schedules a callback function to be executed after a specified delay.
How it works is that the callback function is added to the macro task queue, and the event loop will check the macro task queue for tasks to execute when it has nothing to execute in the call stack.
As we can see in this diagram, the setTimeout
scheduled for 0 milliseconds is added to the macro task queue.
However, as there is more code to execute, the event loop will continue to execute the next line of code first.
Line 2: Promise.resolve().then(() => alert("Promise"));
#
Promise.resolve()
returns a promise that is immediately resolved.
The then
method is called on the promise, which schedules the callback function to be executed in the micro task queue.
As we can see in this diagram, the then
method is called on the promise, which schedules the callback function to be executed in the micro task queue.
However, as there is more code to execute, the event loop will continue to execute the next line of code first.
Line 3: recursion(2);
#
The recursion
function is called with the argument 2
.
It is a recursive function that alerts the value of n
and then calls itself with n - 1
until n
is equal to 0.
This function will all more functions to the call stack, which will be executed immediately.
Line 4: alert("End");
#
As the the last line of code is just an alert, it will be executed immediately.
After Line 4 #
After line 3 is executed, the javascript function exits. In this moment, the event loop will be checked for tasks to execute.
The micro task queue is checked first, and the callback function scheduled by the promise is executed.
This results in the alert box “Promise” being displayed.
After the micro task queue is empty, the event loop will check the macro task queue for tasks to execute.
The callback function scheduled by the setTimeout
is executed, resulting in the alert box “Timeout” being displayed.
Summary #
You can play around in this segment below to see how the event loop works.
In this example, the alert
is replaced with console.log
instead.
Conclusion #
Overall, the event loop is a mechanism that allows JavaScript to handle asynchronous tasks in a non-blocking manner on a single thread.
When programming in JavaScript, it is important to understand how the event loop works and how it manages the execution of tasks using the micro task and macro task queues.