Breno Baptista

Using Asynchronous Functions in JavaScript

When executing an operation that needs a response from an external resource, you have to use asynchronous code to wait until the result has returned what you want before running the next operation. If you don't do that, it will run the next line of code immediately after the previous line without waiting for a valid response.

Table of Contents

Promise object

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation completed successfully.
  • rejected: meaning that the operation failed.

A promise is said to be settled if it is either fulfilled or rejected, but not pending.

Creating promises

Promise

When creating your promises from the ground up (like in your libraries), use the good old Promise with the help of .then.catch.

// creating the promise
const promise = () => new Promise((resolve, reject) => {
  setTimeout(() => resolve("Done!"), 1000)
  setTimeout(() => reject(new Error("Whoops!")), 5000)
})

// using the promise
promise()
  .then(result => console.log(result))
  .catch(error => console.log(error))
  .finally(() => console.log("Promise is settled!")) // optional

Chained Promises

You can use .then.then.then... instead of using .then().catch() inside a .then multiple times.

myPromise
  .then(handleFirst)
  .then(handleSecond)
  .then(handleThird)
  .catch(handleAnyReject)

Other methods

You can check methods like Promise.all and Promise.race on MDN Web Docs.

Async/await

When using a promise somebody else created, use async/await. It's just syntactic sugar for promise use.

async is used to tell JavaScript that the function is asynchronous, while await means "wait for this promise to resolve before running the next line of code".

// imported oldPromise from a library or some file

const newPromise = async () => {
  try {
    const result = await oldPromise()

    return result
  } catch (error) {
    throw new Error(error)
  }
}

When you are at the top level you can't use await, so you need to use .then().catch() in this case.

Reviewing code

Now that you understand async functions in JavaScript, let's analyze the wrong code below I found in real life:

async fetchDashboardData(uid) {
  return await firebase
    .database()
    .ref(`dashboard/${uid}`)
    .once('value')
    .then(snapshot => {
      return snapshot.val()
    })
}

This Firebase method is asynchronous, so the author used await. But notice that at the end we have .then(), so JavaScript already knows this function is async. Also, he is returning the promise (and returning twice, did you notice that?).

We could use either of these three methods to fix that function:

1) Promise

const fetchDashboardData = uid => new Promise((resolve, reject) => {
  firebase
    .database()
    .ref(`dashboard/${uid}`)
    .once('value')
    .then(snapshot => {
      resolve(snapshot.val())
    })
    .catch(error => {
      reject(error)
    })
}

2) .then().catch()

const fetchDashboardData = uid => {
  firebase
    .database()
    .ref(`dashboard/${uid}`)
    .once('value')
    .then(snapshot => {
      return snapshot.val()
    })
    .catch(error => {
      throw new Error(error)
    })
}

3) async/await

const fetchDashboardData = async uid => {
  try {
    const snapshot = await firebase.database().ref(`dashboard/${uid}`).once('value')

    return snapshot.val()
  } catch (error) {
    throw new Error(error)
  }
}

Low-poly portrait of Breno Baptista

Breno Baptista is a software engineer who likes to explore new things every day. He is interested in Linux, open-source software, digital privacy and front-end development.