Get Rid of Callback Hell with Async/Await

Promise has changed the way we think about callbacks in JavaScript. It has also changed the way we write the callbacks and has given a great solution

·

4 min read

Get Rid of Callback Hell with Async/Await

In this article, I will be explaining how to write code that is easier to read and understand using async/await.

Usecase — To search a book by its name from the one API endpoint that has the book ISBN, title and price and another API endpoint to check if that book is in stock or not, then hit another API endpoint to check its pricing, concat all the responses and then send it to front-end.

Firstly, let’s assume an API that returns a book when searched by its name. Here in this example, I’m searching for a book by the name Art of War. This API response returns the book object having name, author & ISBN.

// get books
const getBookByName = (name) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ name: 'Art of War', author: 'Sun Tsu', ISBN: 9780140439199 });
    }, 500);
  });
};

then invoke the getBookByName method using the getBookByNamePromise

getBookByName('Art of War').then((book) => {
  if (book) {
    console.log('Found the book!');
  } else {
    console.log('Book not found!');
  }
});

Once the book details are found, let’s check if the book is in stock or not by invoking another Promise. Here in this Promise, I’m returning the stock & ISBN details.

// check if that book exists
checkIfBookExists = (ISBN) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ stock: 10, ISBN: 9780140439199 });
    }, 500);
  });
};

Then invoke the checkIfBookExists method inside the getBookByName method by passing the ISBN received from the response. The checkIfBookExiststakes the book ISBN as a parameter.

getBookByName('Art of War').then((book) => {
  if (book) {
    checkIfBookExists(book.ISBN).then((data) => {
      console.log(data.stock > 0);
    });
  } else {
    console.log('Book not found!');
  }
});

If the book stock exists, the checkIfBookExists API returns the stock count and also the ISBN. After that let’s check the pricing of the book by using the BooksRun API, the BooksRun API returns the price of the book by ISBN. The response contains new, used and rent prices of any book 🤘

// hit BooksRun API - To get the new, used and rent price
getTheBookPrice = (ISBN) => {
  return new Promise((resolve, reject) => {
    const URL = `https://booksrun.com/API/v3/price/buy/${ISBN}?key=${process.env.API_KEY}`;

    fetch(URL)
      .then((response) => {
        resolve(response.json());
      })
      .catch((err) => {
        reject(err);
      });
  });
};

Then invoke the getTheBookPrice if the stock of the book is greater than 0. Let’s check the price for that particular book

getBookByName('Art of War').then((book) => {
  if (book) {
    checkIfBookExists(book.ISBN).then((data) => {
      if (data.stock > 0) {
        getTheBookPrice(data.ISBN).then((response) => {
          console.log(response.offers.booksrun.new.price); // uff 😅
        });
      }
    });
  } else {
    console.log('Book not found!');
  }
});

In a real scenario, there will be null checks and API calls 🤭 In the above code example, there is a lot of callbacks, then and I’ve tried to avoid writing catch 😂. Now rewriting this whole example using async/await👇

const beingAsync = async () => {
  const book = await getBookByName('Art of War');
  const { stock } = await checkIfBookExists(book.ISBN);
  if (stock > 0) {
    const response = getTheBookPrice(book.ISBN);
    console.log(`Book price is $${response.offers.booksrun.new.price}!`);
  } else {
    console.log('Book not found!');
  }
};

beingAsync();

Using async/await makes the code look cleaner, readable and making debug easy 👍

The whole code snippet for reference 👇

// get books
const getBookByName = (name) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ name: 'Art of War', author: 'Sun Tsu', ISBN: 9780140439199 });
    }, 500);
  });
};

// check if that book exists
checkIfBookExists = (ISBN) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({ stock: 10, ISBN: 9780140439199 });
    }, 500);
  });
};

// hit BooksRun API - To get the new, used and rent price
getTheBookPrice = (ISBN) => {
  return new Promise((resolve, reject) => {
    const URL = `https://booksrun.com/api/v3/price/buy/${ISBN}?key=${process.env.API_KEY}`;

    fetch(URL)
      .then((response) => {
        resolve(response.json());
      })
      .catch((err) => {
        reject(err);
      });
  });
};

// Using Promise
getBookByName('Art of War').then((book) => {
  if (book) {
    checkIfBookExists(book.ISBN).then((data) => {
      if (data.stock > 0) {
        getTheBookPrice(data.ISBN).then((response) => {
          console.log(response);
        });
      }
    });
  } else {
    console.log('Book not found!');
  }
});

// Using async/await
const beingAsync = async () => {
  const book = await getBookByName('Art of War');
  const { stock } = await checkIfBookExists(book.ISBN);

  if (stock > 0) {
    const response = getTheBookPrice(book.ISBN);
    console.log(`Book price is $${response.offers.booksrun.new.price}!`);
  } else {
    console.log('Book not found!');
  }
};

beingAsync();

Thanks for taking the time to read this article! 🙏