The Last Event Loop Guide you will Ever Need
Yes, I said it. This is it.
💡 Read this Guide end to end to understand exactly how the Dreaded Event Loop in Javascript or NodeJS works.
Step 1️⃣ : Synchronous and Asynchronous
As a developer, you need to understand every code you write needs to be executed either Synchronously or Asynchronously.
Synchronous
Synchronous execution is when your program is executed line by line.
Take this code snippet for example :
console.log("Entry 👇🏻");
function add(firstNumber, secondNumber){
return firstNumber + secondNumber;
}
console.log("Function Called : ",add(2,5));
console.log("Below function 🧐");
The output is :
Entry 👇🏻
Function Called : 7
Below function 🧐
Pretty simple right? The code follows a top to bottom approach of execution, with the function returning the sum immediately.
Let's take a different scenario.
Asynchronous
Asynchronous code is simply put to work on a separate context for execution. This doesn't mean that it is not being executed.
💡 Asynchrony, in computer programming, refers to the occurrence of events independent of the main program flow and ways to deal with such events.
This essentially means tasks that can be delegated away from the main thread of execution.
Let's take an example of reading data from a file using NodeJS.
const fs = require("fs");
console.log("Entry 👇🏻");
fs.readFile('sample.txt', 'utf8', (err, data) => {
if (err) {
// Handle error if file reading fails
console.log("Error reading file");
} else {
// Invoke callback with file content
console.log("File reading done. Contents 👇🏻\n",data);
}
});
console.log("Below function 🧐");
Can you guess the Output?
Let me help you.
Entry 👇🏻
Below function 🧐
File reading done. Contents 👇🏻
Hello, I am the content in sample.txt file!!
Can you spot the difference?
This is what asynchronous programming helps us do.
File reading is a CPU heavy process(if for small files, this takes more time than a simple program execution).
But this is not the reason that the contents are displayed after the console.log
after the file reading method.
This happens because NodeJS defines fs.readFile
as an asynchronous task. And NodeJS gives priority to Synchronous tasks over Asynchronous tasks.
Step 2️⃣ : Understanding the NodeJS environment
NodeJS is a Library built to run Javascript outside of the browser.
We will not get into the details of why and how this is done.
But let's get into how NodeJS handles the execution of a program.
NodeJS internally has 3 main parts that handle the execution of a Javascript program.
For the above 2 programs, let's see individually how NodeJS handles execution internally using the images below(for Better Understanding) :
First Program :
To understand the working of the second program, read the section below with the simulation
Step 3️⃣ : Understanding the Thread Pool and Callback Queue
For all the non-blocking code, it passed to the Thread Pool implemented using LibUV library.
The threads are tasked with executing the Asynchronous tasks outside of the main thread. This helps NodeJS execute so many network calls at once, even though Javascript is a Single-Threaded Language
.
The Threads process the tasks and pass the callbacks into the callback queue.
The Callbacks are queued according to a certain priority, which we will discuss in the below sections.
💡 By default there are 4 working threads in Libuv. But you can increase them upto : MAXIMUM NO. OF CPU CORE AVAILABLE which you can get using the code below :
const max = os.cpus().length;
- Callbacks are picked up by the Event Loop and pushed onto to the Call Stack of Javascript, ONLY WHEN THE CALLSTACK IS EMPTY.
Step 4️⃣ : The Event Loop
The Event Loop is nothing but a scheduling algorithm.
Yes, it is that simple.
A scheduling algorithm that defines which callbacks are to be prioritized over the others.
But before understanding the order, understand that there are 6 types of callbacks, based on the calling instance.
6 Types of callbacks :
Timer Callbacks : You might have used the
setTimeout
and thesetInterval
methods.I/O Callbacks : Input/Output operations are the File reading and Network based operations.
Check Callbacks : There is a NodeJS specific function :
setImmediate
used to execute a Callback as soon as the current event loop finishes.
This is basically like the Hooks in ReactJS, which helps us hook into the lifecycle of NodeJS execution.Close Callbacks : For the I/O operations, we have event listeners to listen to the
closing
event. Once the event is fired, these callbacks are defined to be executed.Promise Callbacks : When promises are invoked, we supply callbacks to be called based on the result of the promise.
Next Tick Callbacks : Next Tick is a function native to NodeJS modules (
process.nextTick()
) used by developers in real-time applications every day to defer the execution of a function until the next Event Loop Iteration.
The Event Loop schedules the callbacks, which is visualized in the following diagram :
I know it might be overwhelming, but is simple.
The steps are as follows :
Check if callstack is Empty, then Proceed as follows.
Callbacks in the Microtask Queue which is divided in 2 parts :
i) Execute thenextTick
callbacks.
ii) Execute thePromise
callbacks.-
Callbacks in the Timer Queue, but there is a catch here.
🚨 After every callback in the Timer Queue is executed, REPEAT STEP 2(
Microtask Queue
) before moving to next callback in Timer Queue. Execute callbacks in the I/O Queue.
REPEAT STEP 2 (
Microtask Queue
)-
Execute Callbacks in the Check Queue i.e. the
setImmediate
callbacks, but there is a catch again.🚨 After every callback in the Timer Queue is executed, REPEAT STEP 2(
Microtask Queue
) before moving to next callback in Timer Queue. Execute all the callbacks in the Close Queue
REPEAT STEP 2 (
Microtask Queue
)Check if there process needs to repeated. If NOT 👉🏻 then EXIT.
This is the Event Loop steps simplified.
But we are Developers. Of-course we need examples.
So here are a few links which you can use for examples :