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

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));
    });
  });
}