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