How to avoid callback hell while you are working with server side Javascript
What is callback hell?
This is a big issue caused by coding with complex nested callbacks. Here, each and every callback takes an argument that is a result of the previous callbacks. In this manner, The code structure looks like a pyramid, making it difficult to read and maintain. Also, if there is an error in one function, then all other functions get affected.
Example code with nested callback
The following example will fetch user details from an API first, then get all the articles of that particular user, then comments of the first article. So three levels of nested callbacks we can see here. Sometimes this will make code little bit difficult to maintain.
const request = require("request");
request(
"https://jsonplaceholder.typicode.com/users/1",
function (error, response, userBody) {
if (error) {
console.log("Error: " + error);
}
const userInfo = JSON.parse(userBody);
request(
`https://jsonplaceholder.typicode.com/posts?userId=${userInfo.id}`,
function (error, response, userPostBody) {
if (error) {
console.log("Error: " + error);
}
const userPosts = JSON.parse(userPostBody);
const firstPost = [...userPosts].shift(); //will get first post from the post array by removing that post from userPosts array
request(
`https://jsonplaceholder.typicode.com/posts/${firstPost.id}/comments`,
function (error, response, commentsBody) {
if (error) {
console.log("Error: " + error);
}
const userComments = JSON.parse(commentsBody);
console.log(userComments);
}
);
}
);
}
);
Solved callback hell using Promises and async/await
const request = require("request");
function fetchAPI(url) {
return new Promise((resolve, reject) => {
request(url, function (err, res, body) {
if (err) reject(err);
else resolve(JSON.parse(body));
});
});
}
async function fetchComments() {
const userInfo = await fetchAPI(
"https://jsonplaceholder.typicode.com/users/1"
);
const userPosts = await fetchAPI(
`https://jsonplaceholder.typicode.com/posts?userId=${userInfo.id}`
);
const firstPost = [...userPosts].shift(); //will get first post from the post array by removing that post from userPosts array
const userComments = await fetchAPI(
`https://jsonplaceholder.typicode.com/posts/${firstPost.id}/comments`
);
console.log(userComments);
}
fetchComments();
You can checkout this code from the repository https://github.com/vimson/hands-on/tree/main/callback-hell and follow the below steps to experiment with this
How to run this on your local development machine
- Checkout this repository
- Go to the
callback-hell
directory - Perform
npm install
- Run
node without_callbacks.js
to run the code with callbacks - Run
node with_callbacks.js
to run the code without callbacks using async/await - Refer MDN Async_await for more
How to make callback-based functions return a promise
Wrap the function in another function which returns a promise. It then resolves or rejects based on callback arguments.
Example:
function fetchAPI(url) {
return new Promise((resolve, reject) => {
request(url, function (err, res, body) {
if (err) reject(err);
else resolve(JSON.parse(body));
});
});
}