The Callback function

Aditya Channe
DataDrivenInvestor
Published in
7 min readApr 19, 2021

--

Hey, I hope you are doing well today. It is my first article in 2021, and I know it's pretty late, though, but I wish you all a happy new year! I hope this year brings a lot more positivity and happiness all around. All right, so I talked about the Node.js architecture last time, where I discussed individual things that make Node.js stand out in the competition. This article will be very specific and concentrate on just one element, callback functions in Node.js. The callback functions are the core of Node.js. Node is designed around the callback functions, making it very important to understand well. So let's begin.

Wait. Before starting with the callback functions, you must understand the Synchronous and Asynchronous code.

Synchronous or Blocking Code

Synchronous or blocking code is the one that processes each line one by one. Let's use an example to understand this.

const fs= require('fs');const data=fs.readFileSync(’file.txt’);    
console.log(data);
nextFunction();

Let's take this simple example. Forget about the code if you don't understand now because that's not important for now. I will cover this code in future writeups or later search it on the internet. What is important here is that Each line of code here will execute one by one. Each line will have to wait for the result of the code from the previous line.

const data= fs.readFileSync(’file.txt’); //1

The program will be blocked here until the file has finished reading it.

console.log(data);         //2

Once the file finishes reading, the following line is processed.

nextFunction()            //3

After the compiler finishes executing the current line (line number 2), it is also known as a blocking code. It is only possible to perform a particular operation before it is completed. The 2nd operation above will execute when the 1st operation is finished, and it will only perform the 3rd operation if the 2nd operation is finished.

Now, why is this a problem now?

Each line will block the execution of the rest of the code until it finishes executing the current line. And in the case of a huge file, it will make operations very slow. Let's take it the other way for better understanding. It will make the end-user wait for a longer time to access the data required, and they may walk away, something that we would not want to.

What does Synchronous code mean for node?

Now, the way node.js is designed, synchronous code was a significant issue. As I said, in a node.js process, where our application is running, there is only one thread. Thread is where our code is executed on the processor of the machine. The key point is that Node.js is single-threaded and has only one thread for each application. This means that all the users accessing the application are using the same thread. Whenever you interact with the application, the code running for each user will run in the same thread at the same place on the computer running the application, no matter if you have one user or five users or 5 million users.

Now, what this also means is that when one user blocks the thread with the blocking code, as we've just seen in the example before, all the other users will have to wait until the current execution is over, and that might not be a problem if you have limited or fewer users. Still, it will be a massive problem if you have thousands or millions of users at the same time.

Imagine a user is accessing your application, and there is a large file to read in your code that takes like three seconds to load. It will mean that all the other users will have to wait for the next three seconds because the file blocks the entire execution for three seconds, and they can do nothing but wait for the process to finish.

It is a very oversimplified way of what exactly happens when a Synchronous code is executed in Node.js for you to understand its logic.

The Solution

It is a miserable experience for your users to wait for their tasks even to start the execution. As a developer, it's your responsibility to prevent these types of situations. The answer to this problem is Asynchronous or Non-Blocking code.

Asynchronous or Non-Blocking Code

The thread will serve multiple requests at the same time in the Asynchronous code. It will offload the heavy task like reading the file in the above example to run in the background where it effectively resides until the data is done reading. When the result is generated, a function (callback function) will be called to handle the result. It will allow the other users to perform the task in a single thread without waiting until the first task is completed. The operations are transferred to another system in the background instead of the process being blocked so that the thread can execute the next piece of code.

This time, let's understand this in the same example but with Asynchronous code.

const fs= require('fs');fs.readFile('/file.txt', (err, data) => { 
if (err)
throw err;
console.log(data);
});
nextFunction();

Here, each line will not have to wait for the last line to finish the execution, which means that the current code's execution will not block the rest of the code.

fs.readFile('/file.txt', (err, data) => { 
if (err)
throw err;
console.log(data);
});

The file file.txt will start reading, but this time it will not run in the same thread, but it will be moved to a different thread to execute, and the main thread will be free to execute the following code.

nextFunction();

The next function will immediately start executing while the file.txt file is still finishing in the background.

The Callback Function

Now we have the idea that in Asynchronous code, heavy, time-consuming, and slow operations are offloaded to complete in the background. But how do we access the data from the file running in the background, or how do we know when the file has finished the execution and needs to be called? The answer is simple, using the callback function. We use callback functions to handle the asynchronous code in the background. When the output is generated, the callback function is called to be executed in the main single thread to process the results.

Let's see the callback function in execution.

fs.readFile('/file.txt', (err, data) => { 
if (err)
throw err;
console.log(data);
});

Here the fs.readFile is the asynchronous way of reading a file. Everything after the file name is part of the callback function.

(err, data) => { 
if (err)
throw err;
console.log(data);
});

The callback function takes in two arguments err and data. If the file in execution returns an error, the error argument will be called, and if there is no error, the data argument will be called.

So when the file or any operation is executing in the background, the callback function waits for the process to complete execution. Based on whether there was an error or no error, the response is called by the callback function in the main thread to return the result without blocking the main thread during the file's execution for the entire time. It is the callback function.

That's it. That's an overview of how Node.js manages asynchronous behavior to implement the non-blocking I/O model I discussed in the last post. I hope it makes more sense now why callback functions and asynchronous code is essential for the node. That's the whole reason why Node.js is built around callbacks entirely. It makes node.js the perfect choice for creating web applications that are highly efficient and scalable. It works completely differently in other programming languages, such as PHP, because you have one new thread for each new user, which is a different approach and works entirely differently.

Alright, so thanks for stopping by to this article. I hope this was helpful to you. Do let me know what you think about node.js now. See you in the next post.

--

--

Hello and welcome to my blogs! It’s very easy. I write about what I do and how I think it may help people who want to start building things using code.