Node.js Callbacks

    0 Votes

One of the most confusing concept in programming is used in NodeJS - the Callbacks. Even after its complexity, it is now used in other languages (like Go) as well. This article exposes you to the amazing, confusing but highly used concept of Callbacks.

Again let us continue with the Hello World Application. But first you need to understand about threads.

Threads

A thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system. Imagine threads in the following way:
Suppose you have a team of chefs to cook mean for a party. Now it is evident that one person cannot cook alone. Suppose there are 5 chefs in total and each one is in charge of a particular task. They all work together to perform a complete task. Also if a chef is doing a particular task he won’t be able to do anything else till he finishes his ongoing task.

Threads in an Operating System are similar to these chefs. Each thread handles a particular task and is incapable of doing some other task while it is doing something.

Hello World Application - 3

From the article on Global Objects, we have the files index.js and myFolder/myTextFile.txt . Here is the index.js

"use strict"; // Enable Strict Mode
const fs = require('fs'); // Import 'fs' module
const path = require('path'); // Import 'fs' module
let readMyText = function() 
{
	let str = fs.readFileSync(path.join("myfolder","myTextFile.txt")).tostring(); 
	console.log(str); //Print the string
};
readMyText();

See line 6:

fs.readFileSync(), as the name suggests, reads the text file synchronously i.e. the read operation occurs on the same thread which is executing the script. This causes the thread to block all other operations and requests which might come to the script. If this problem occurs in a web server, a client will not be able to connect with the server unless the previous client will has got its response. This would lead to a lot of delay and wait which is undesirable.

As a solution to blocking scripts, we pass these synchronous functions to worker threads from NodeJS internal architecture with a system to execute the next line only after the intended function is finished that too without blocking the main thread.

Now let’s see a code which runs asynchronously and returns the string.

"use strict"; // Enable Strict Mode
const fs = require('fs'); // Import 'fs' module
const path = require('path'); // Import 'fs' module
let readMyText = function() 
{
	fs.readFile(path.join(_dirname, "myfolder","myTextFile.txt"), function(err,str)
	{
	 console.log("\n\nInside Read File");
	 console.log(str.toString());
	}
	console.log("----Outside readFile-----");
};
readMyText();

Explanation:

Inside the readMyText() function we call the readFile() method. fs module exposes a readFile() method which is used for reading the contents of a file asynchronously.

fs.readFile() function takes two arguments:

  1. path of the file to read (path.join(__dirname, "myFolder", "myTextFile.txt"))

  2. A piece of code to be executed when the reading function has finished executing. This piece of code (function) which is passed as an argument to another function is called a Callback.

See the output:

callback flowchart

You might be confused about how the order of logging changed. Try to understand with the following flow chart.

callback outout

On the start of the program, the main thread starts and calls the readMyText() functions as written in the program. When the readMyText() function is executed, the first line fs.readFile() passes the control (the path and the to be executed function) to a background thread.

That thread executes in the background without interacting with the main thread. While the file reading is taking place, say it takes 5 seconds, the main thread is not waiting for the background thread to finish. Instead it executes console.log(“Outside readFile”) function and prints to console.

Once the main thread has executed every line once, node checks if there is any background thread. In the presence of a running background thread, node waits for all the background threads to finish before exiting the process.

The background thread on the other hand executes independently and after reading the file for 5 seconds, it executes the code that is passed as an argument to the fs.readFile() function. Completely independent of the main thread.

It has the benefit that while the background thread is executing some function, the main thread remains free and continues to accept more functions (if any) or client requests and processes them using background thread. This is NON-BLOCKING structure of NodeJS which makes it blazingly fast.

Myth breaking

Now one thing to note is that every function which has a callback function used in it is not necessarily an asynchronous function. Consider the following functions:

"use strict"; 
let mainFunction = function() 
{
	console.log("main Function");
	secondFunction(function(str)
	{
	  console.log("String from secondFunction : " + str);
	});
	console.log("End of main function");
}
let secondFunction = function(callback)
{
  console.log("Inside second function");
  console.log("Argument to callback");
}
mainFunction();

The output of the shown script is :

callback function output

Surprising isn't it?

Well the script behaves as a synchronous script because the control is never passed on to a background thread. Everything is done in the main thread only.

You can use callbacks even for synchronous scripts but it is not encouraged as it makes debugging difficult.

To pass the control over to a background thread, you need to use any of the following.

  1. Process.nextTick();

  2. setTimeout();

  3. Node's internal libuv library to handle thread creations.

Let’ s make the above script asynchronous.

"use strict"; 
let mainFunction = function() 
{
	console.log("main Function");
	secondFunction(function(str)
	{
	  console.log("String from secondFunction : " + str);
	});
	console.log("End of main function");
}

let secondFunction = function(callback)
{
  setTimeout(function(){
    console.log("Inside second function");
    console.log("Argument to callback");
  }, 1000);
}
mainFunction();

The setTimeout function is another global function which delays the execution of some instructions by a fixed specified duration.

asynchronous script output

In this example, on invoking the mainFunction(), it prints console.log(“main Function”); as expected. Next it calls the secondFunction(). In the secondFunction() there is a setTimeout of 1000ms. Due to the NON-BLOCKING structure of NodeJS, the main thread passes the argument of setTimeout function to a background thread and returns to mainFunction() where it executes console.log(“End of main function”); . After the timeout period, the background thread executes the specified function and voila you just made an asynchronous function.

This is the concept about the confusing CALLBACKS.

Popular Videos

communication

How to improve your Interview, Salary Negotiation, Communication & Presentation Skills.

Got a tip or Question?
Let us know

Related Articles

Node.js Introduction & Environment Setup
Node Package Manager (NPM)
Global Variables in Node.js
Event Loop & Event Emitters in Node.js
Node.js Buffers and Streams
Node.js File System & Setting up File Structure