Asynchronous Node.js: Callback, Promises and async/await in JavaScript
For the majority of us, learning asynchronous programming can be difficult but if you want to improve the user experience in your app and have Node.js in your tech stack then its a critical area to learn and master. This is because JS code is asynchronous by default.
Before the Node version 7.6, callbacks were the only way to run one function after another in the Node. However, using callbacks on the single-threaded architecture can cause certain issues as every callback takes an argument that is a result of a previous callback. Any error in a single function affects all other functions. Also, the code structure is difficult to read and maintain.
Such a situation is commonly called callback hell and so to find the way out, the concept of Promises and function chaining was introduced. But to implement the promises one needs to be a master in it. As it requires understanding what will be the Promise results.
In this article, we are going to learn about asynchronous programming in Node.js and how to simplify it using the Promises and the whole new way: async/await keywords.
So, what are you waiting for, lets get started!
What is asynchronous programming in Node.js?
Asynchronous programming also called non-blocking code(in Node.js) allows a program to continue execution of other code while waiting for the heavy-running tasks to complete without freezing the program.
Node.js runs single-threaded JavaScript code in the event loop and offers a background Worker Pool to handle heavy operations such as file I/O. This helps to make the Node.js application scalable, but on the other hand, its important to structure your application wisely as it runs on a few threads.
Node.js server architecture
If you look into the Node.js server architecture it mainly comprises:
- Single-Threaded Event loop: The main thread that process request and gives result both synchronous and asynchronous.
- Event queue: Stores incoming request and passes them one by one to the event loop.
- Worker Pool/ background threads: Threads available to carry out heavy background tasks.
- Incoming Request: Incoming requests can be blocking or non-blocking
- Other resources: Used to deal with the blocking clients such as computation, database, and file system
Now lets understand how it works.
The client sends a request to the webserver to interact with the app, the request can be blocking or nonblocking. Node.js gets these requests and adds them to the Event Queue. All these requests are passed to Event Loop one by one.
Then it is checked that requests are simple or not i.e blocking or non-blocking. The event loop processes a simple request and a single thread from the Worker Pool is allocated to complete the complex blocking request by accessing the external resources.
Once, the processing is done response is sent to the Event Loop which is then sent to the Client.
The Node.js application will work fast if the work associated with each client at any given time is small, and it applies to the callbacks on the Event Loop and tasks on the Event Pool.
So, now you have got a clear idea about how Node.js single-thread architecture works. So. lets dive into the async/await.
The history and the future of the asynchronous JavaScript
Now, we will look into the time, how the implementation of asynchronous programming evolved from the original callback to the new shiny async/await.
Callbacks in JavaScript(The old way)
Callbacks are the functions that are executed at the end of synchronous or I/O blocking operations and pass an argument to another function. Here is a simple example of a callback. You can call the myCalc function with the callback and then let the myCalc function run the callback once the calculation is completed.
function myDisplay(some) {
document.getElementById("demo").innerHTML = some;
}
function myCalc(num1, num2, myCallback) {
let sum = num1 + num2;
myCallback(sum);
}
myCalc(5, 5, myDisplay);
The above example doesnt show you how to use callback in the asynchronous function. A good example of an asynchronous function in JavaScript is setTimeout().
function myFunction() {
document.getElementById("demo").innerHTML = "Hello"
}
setTimeout(myFunction, 3000);
In the above example myFunction() is used as a callback, it is passed to the setTimeout function as an argument. The setTimeout will wait for a certain number of milliseconds before executing the callback.
Though it looks convenient to attach a callback to the blocking operations, it introduces some issues such as callback hell and you cant decide how the higher-order function will execute it.
Therefore we need a new way that will enforce better practices to avoid such problems.
Promises in JavaScript(The new way)
Promises were introduced to fix most of the problems with using the callbacks to perform asynchronous operations.
A promise is a Javascript object that links the producing code(that takes some time to fetch results) and consuming code(the code that must wait for the result), in this way promises helps developers to stick to the specific convention and chain the callback functions to well-aligned to the top-down flow.
The Promise object in Javascript can be pending(working), Completed(result is received), or rejected(a result is an error object). Here is a straightforward example of using Promises.
let myPromise = new Promise(function(myResolve, myReject) {
let req = new XMLHttpRequest();
req.open('GET', "mycar.htm");
req.onload = function() {
if (req.status == 400) {
myResolve(req.response);
} else {
myReject("File not Found");
}
};
req.send();
});
myPromise.then(
function(value) {myDisplayer(value);},
function(error) {myDisplayer(error);}
);
However, the promises still have some problems and are difficult to write and understand.
Async await JavaScript: Make Promises easier to write
Async await solves the memory sharing problem of Promises and makes the code much more readable. Also, both the keywords async and await are very easy to use.
Async makes the function return a Promise and await makes a function wait for a Promise, you will better understand it through the following example.
async function getFile() {
let myPromise = new Promise(function(resolve) {
let req = new XMLHttpRequest();
req.open('GET', "mycar.html");
req.onload = function() {
if (req.status == 200) {
resolve(req.response);
} else {
resolve("File not Found");
}
};
req.send();
});
document.getElementById("demo").innerHTML = await myPromise;
}
getFile();
Summing up:
So, this is all about the asynchronous programming in Node.js and its evolution from callbacks, then to Promises, and now to async/ await.
Want to power up your backend Node.js development? Try DhiWise : A new generation app development platform for clean, scalable, and faster app development.
Top comments (0)