Promises

What is a Promise?

A promise is an object that represents the eventual resolution (success or failure) of an asynchronous operation. A promise can be in one of three states:

  • pending - The action has not yet been resolved or rejected

  • resolved - The action relating to the promise succeeded

  • rejected - The action relating to the promise failed

While the promise is active it is in a pending state and at some point will either be resolved or rejected.

Handling A Promise

The Promise will be expected to return some value from either the resolve or reject methods which make use of the following methods:

  • .then() - works with a resolved promise

  • .catch() - works with a rejected promise

  • .finally() - will execute some code regardless

A resolved promise uses .then() to handle the data returned via the promise.

Resolved

somePromise.then((value) => {
  console.log('value:', value)
})

A rejected promise uses .catch() to handle any errors returned via the promise.

Rejected

somePromise.catch((error) => {
  console.log('error:', error)
})

Any resolved or rejected promise can make use of .finally() to perform any final action at the end of the promise chain.

somePromise
    .then((value) => {
      console.log('value:', value)
    })
    .catch((error) => {
      console.log('error:', error)
    })
    .finally(() => {
      console.log('do stuff whatever happens to the promise')
    })

Chaining Methods

Quite often the return values of a promise are passed to other .then() methods that perform some additional task.

// Pretend somePromise resolves with the number 1
somePromise
  .then((val) => {
    console.log("first then:", val) // 1
    return val + 2;
  })
  .then((val) => {
    console.log("second them:", val) // 3
  })

Making An API Call

One of the most common use cases of using Promises is when making an API call. Since making a request to an external server will take time we will need to wait for the request to either return some data (resolve) or notify us that there has been an error (reject).

Here is an example of using axios:

axios.get resolves with a response object (res).

axios
  .get('https://dummyjson.com/products/')
  .then((res) => {
    console.log(res.data)    // => The data
    console.log(res.status)  // => 200
  })

To handle errors we can add a .catch at the end of the chain.

axios
  .get('https://dummyjson.com/products/2000')
  .then((res) => {
    console.log(res.data)
    console.log(res.status) 
  })
  .catch((err) => {
    console.log(err.response.data.message) // => Product with id '2000' not found
    console.log(err.response.status)       // => 404
  })

A more complex example

Let's load products from a random category. We'll need to make two API calls, the first is to get all the categories and the second is to get products from the randomly chosen category.

Using axios

const baseURL = 'https://dummyjson.com/products'

const pickRandom = ({ data }) => {
  return data[Math.floor(Math.random() * data.length)]
}

const getProducts = (category) => {
  return axios.get(`${baseURL}/category/${category}`)
}

const logData = ({ data }) => {
  console.log('Random Products')
  console.log(data.products)
}

const handleErrors = (err) => {
  console.log(err.response.data.message)
  console.log(err.response.status)
}

axios
  .get(`${baseURL}/categories`)
  .then(pickRandom)
  .then(getProducts)
  .then(logData)
  .catch(handleErrors)

Using fetch

const baseURL = 'https://dummyjson.com/products'

const pickRandom = (arr) => {
  return arr[Math.floor(Math.random() * arr.length)]
}

const extractJSON = (res) => {
  return res.json()
}

const getProducts = (category) => {
  return fetch(`${baseURL}/category/${category}`)
}

const handleErrors = (err) => {
  console.log('Uh oh', err)
}

fetch(`${baseURL}/categories`)
  .then(extractJSON)
  .then(pickRandom)
  .then(getProducts)
  .then(extractJSON)
  .then(console.log)
  .catch(handleErrors)

If Promises hadn't been created, we would have had to use callback functions to manage the asynchronicity. A version of the code above using a hypothetical callback-based fetch would look something like this:

Callback Hell

fetch(baseURL + '/categories', (err, res) => {
  if (err) {
    console.log('Uh oh!')
  } else {
    res.json((err, categories) => {
      if (err) {
        console.log('Uh oh!')
      } else {
        const category = pickRandom(categories)
        fetch(baseURL + '/category/' + category, (err, res) => {
          if (err) {
            console.log('Uh oh!')
          } else {
            res.json((err, products) => {
              console.log(products)
            })
          }
        })
      }
    })
  }
}) // end Pyramid of DOOM

Last updated