async/await was introduced to solve an issue that Promises was trying to move away from, the callback hell.

With Promises, chaining several .then(cb)s makes the code hard to read/follow. async/await solves this issue by doing away with .then(cb) as we shall see.

async/await does not replace promises but changes how we ‘consume’ Promises.

async/await is built on Promises and Generators and was introduced in ES2017 as a new spec.

Doing async/await correctly

It’s easy to mix async/await with the .then(cb) syntax that Promises had to offer in ES6:

async/await and Promises are inter-operable. With async/await offering a better way of consuming Promises

Promises in summary

Promises represent proxy values that will be available in the future depending on whether the Promise fulfills with a value or is rejected with an error

A promise can be in any of one of these states:

  1. pending state - Initial state, operation not fulfilled/rejected
  2. fulfilled state - operation completely successfully
  3. rejected state- operation failed

a code snippet screenshot

Image credits MDN

Example in code:

// creating a promise
const fetchFakeUser = () => {

  return new Promise((resolve, reject) => {
    // simulate a delay e.g when fetching data over HTTP
    setTimeout(() => {
       resolve({user: 'Itachi Uchiha', token: 'e66d515e-6cbe-46d0-9f22-8705a17b5b6d'})
    }, 5000) // delay for 5 seconds 

// consuming the promise

  .then(data => {
    // use the data(value) resolved in the promise
    console.log(data) // {user: 'Itachi Uchiha', token: 'e66d515e-6cbe-46d0-9f22-8705a17b5b6d'}

Promises can be chained: multiple .then()s can be chained on the promise

const myPromise = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
    }, 2000)

  .then(val => {return val + 1}) // 11
  .then(val => {return val + 1}) //12
  .catch(err => console.log(err))

.catch(cb) handles any error from promise rejection.

How async/await changes how we consume Promises

Async await does away with the .then() and employs async and await when consuming a Promise.

The above becomes:

const main = async () => {
  try {
    const val = await myPromise()
    return val
  } catch(err) {
    // handle error from promise rejection here

We make our function async and append await to myPromise() call then use the returned value as one would in a regular function.

Where the mix up comes in:

Mixing async/await and .then

For example:(from the snippet above:) it’s tempting to do:

const user = await fetchFakeUser().then(user => {
  if(user) return user
  // preceding lines of code
}).catch(err => {
  // do something


async/await does away with the need to use .then(cb) and .catch(cb) when consuming promises. All we need is to:

  1. Append async to the function that will be consuming a promise to use await
  2. Wrap call to the function that returns a Promise inside a try...catch(e) {} block
  3. Store the value return from the function in a variable and prepend await to the function returning a promise.

Note: Always remember to prepend await to a function call that returns a promise