In this article, I will continue to discuss asynchronous code and more specifically how to use async/await.
Before going any further, if there is one thing that I want you to take away from this article is this: At the end of the day, async/await allows us to write async code in a synchronous fashion instead of endlessly nesting callbacks.
main points
When we use async/await, await will always waits till the promise is settled.
We can only use await when we have async in front of the function, this is important because most of the time once people learn async/await especially beginners, they pretty much start using await all over their code and you can not do that. You can only use await if the function that you have set up is actually async.
async function always returns a promise.
N.B: Throughout this article, I'll be using the arrow function but if you prefer using the traditional function there is no law against you, just go for it.
General example
//Showcasing how async function will always return a promise
const example = async ()=> {
return "Hello world"
}
console.log(example()) // it will show a promise in the console.
output:
As you can see, it is a promise and it is right away fulfilled. Just keep in mind that async will always and always return a promise.
//showcasing how await works:
const example = async () =>{
return "Hello world";
}
const someFunc = async () =>{
const result = await example();
console.log(result);
}
someFunc();
output:
From here, await will need to wait until the promise from the example function is settled, then you can get the result in the console. It is very important since you don't have to use .then() chains instead you'll wait for the promise to be resolved and get the result.
Real-world example:
Usually, you will typically request resources over the wire, meaning from the database, API, or whatever but here I'm gonna be using a simple example where I have a user array(containing students) and classes array (containing subjects) students are taking.
const users = [ // array of objects containing stundets and their ID
{id: 1, name:"kebean"},
{id: 2, name:"Chico"},
{id:3, name:"Maelle"},
]
const classes = [ // array of objects containing student ID and classes they are taking
{studentId:1, classesTaken:["OOP", "C", ".Net"]}, //kebean take this class because the ID matches
{studentId:2, classesTaken:["Math", "History"]}, //Chico take this class because the ID mathches
{studentId:3, classesTaken:["Physics", "Chemistry", "Geography"]} //Maelle take this class because the ID mathches
]
const getUser = (name) =>{
return new Promise((resolve, reject) =>{
const user = users.find((user) => user.name ===name);
if (user){
return resolve(user);
}else{
reject(`No such user with name: ${name}`);
}
})
}
const getClasses = studentId => {
return new Promise((resolve, reject) =>{
const userClasses = classes.find((user) => user.studentId === studentId)
if(userClasses){
return resolve(userClasses.classesTaken)
}else{
reject("Wrong ID")
}
})
}
N.B: You need to understand that the above functions happen in sequence, only once you get the student, only then you can access the subject they are studying
Since you are familiar with the above two functions, I want to showcase how you can get the data using the typical .then() approach and later I will showcase how you can achieve the same thing while using async/await which is more effective and readable at least in my point of view.
typical .then() approach
getUser("kebean") // returns a promise
.then((user) => console.log(user)) //you will have access to data you pass into resolve()
.catch((error) => console.log(error)) //when there is errors, you will catch them here
The logic is the following: you pass in the name if the name matches, you will of course get the student and when the name doesn't match, you will get the error message.
output:
when the user doesn't exist:
getUser("kebeans")
.then((user) => console.log(user))
.catch((error) => console.log(error))
As you can see, the error message will be displayed
output:
getUser("kebean")
.then((user) => getClasses(user.id)) //invoking getClasses and pass in the user ID
.then((classes) => console.log(classes)) //the getClasses function above, returns classes that match to the user ID or student ID you passed in
.catch((error) => console.log(error))
output:
The problem with the above setup is once you have multiple async operations, you will have to start chaining these .then() which is not readable and long to write. This is where async/await truly shines because you can make the above code way more readable by refactoring the code to use async/await.
async/await approach
const getStudent = async () =>{
const user = await getUser("kebean")
console.log(user)
}
getStudent()
output:
From the above example, you can see that it's possible to access the student using async/await. Let's see also how to access them classes a student can actually take.
const getStudent = async () =>{
const user = await getUser("kebean");
if(user){
const classes = await getClasses(user.id);
console.log(classes)
}
}
getStudent()
output:
BOOM!! you can get the classes kebean is taking as well.
Just take a look at the code in both approaches, I mean even if the code is pretty much doing the same thing, you can clearly see that the approach of using async/await is way more readable and short.
How to Handle errors when using async/await
As you can see, the error can't be handled as expected while using async/await. take a look at the following code:
const getStudent = async () =>{
const user = await getUser("kebeans");
if(user){
const classes = await getClasses(user.id);
console.log(classes)
}
}
getStudent()
output:
As you can see, the student(user) "kebeans" (with "s" at the end) doesn't exist and instead of getting the error message defined in reject(), you are getting messy in a console and what you need is to handle that gracefully. You can do that by setting up try and catch block like this:
const getStudent = async () =>{
try {
const user = await getUser("kebeans");
if(user){
const classes = await getClasses(user.id);
console.log(classes)
}
} catch (error) {
console.log(error)
}
}
getStudent()
output:
conclusion
Hopefully, now you can clearly see why async/await has become pretty much the go-to when we need to handle asynchronous tasks.
It improves readability by allowing us to write asynchronous code in a more synchronous style, making it easier to read and understand. This can also be particularly helpful for developers who are new to asynchronous programming.
Thanks for reading and happy coding!!
Kebean.