This Q&A is aimed to give clear answers to the following questions:
async
, await
keywords in JavaScript and how do they relate to async functions?To follow the answer the following prerequisites are required:
JavaScript has an asynchronous model. Whenever an asynchronous action is completed you often want to execute some code afterwards. First callbacks were often used to solve this problem. However, when programming code with multiple asynchronous elements a problem arises using callbacks. Because when you nest multiple callbacks inside each other the code becomes hard to maintain very fast. This anti-pattern is called callback hell.
Promises solved many of the issues that appeared in nested callbacks. A key property of promises are that they can be nicely chained together using a promise chain. This allows for much cleaner syntax than callbacks and easier error handling. Here is an example:
const randomProm = new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resolve('Succes');
} else {
reject('Failure');
}
});
// Promise chain
randomProm
.then((value) => {
console.log('inside then1');
console.log(value);
return value
}).then((value) => {
console.log('inside then2');
console.log(value);
return value
}).catch((value) => {
console.log('inside catch');
console.log(value);
});
Asynchronous functions are built on top of promises. They allow a more convenient use of Promises. Asynchronous functions have the following properties:
async
before a function declaration/expression turns the function into an async function. As the name suggests async function are executed asynchronously.Promise.resolve(returnval)
. However, when an uncaught error is thrown inside the async function it wraps the return value in Promise.catch(returnval)
.await
keyword which can be used before any promise. await
makes JS code execution stop until the promise is settled. i.e. The promise has to be either fulfilled or rejected until any further code inside the async function is executed.await
either returns the value of the fulfilled promise, or throws an error in the case of a rejected promise. We can use regular try-catch to catch the error.Let's clarify this with some examples:
const randomProm = new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resolve("Succes");
} else {
reject("Failure");
}
});
// async keyword creates an async function which returns a promise
async function ansyncExample() {
try {
const outcome = await randomProm;
console.log(outcome);
} catch (error) {
console.log(error);
}
// This return value is wrapped in a promise
return 'AsyncReturnVal';
}
// ansyncExample() returns a promise, we can call its corresponding then method
ansyncExample().then((value) => {
console.log(value);
});
console.log('I get executed before the async code because this is synchronous');
// We can use async in function expressions
const randomProm = async () => {
if (Math.random() > 0.5) {
// This value is wrapped in Promise.resolve()
return "Succes";
} else {
// This value is wrapped in Promise.reject()
throw "Failure";
}
};
// async keyword creates an async function which returns a promise
async function ansyncExample() {
// randomProm is async fuction which returns a promise which we can await
return await randomProm();
}
// ansyncExample() returns a promise, we can call its corresponding then/catch method
ansyncExample()
.then((value) => {
console.log("Inside then");
console.log(value);
})
.catch((value) => {
console.log("Inside catch");
console.log(value);
});
console.log("I get executed before the async code because this is synchronous");
In theory you could use async function everytime when you are using a promise. However the power of async functions really starts to shine when there are multiple async operations that return promises and which depend on each other.
Because async function allow us to write asynchronous promise based code in a synchronous manner. The code is still asynchronous but we can now read it in a synchronous manner. It is more easily read and maintained than promise chains and thus scales better (IMO).
Here is an example of this nice readability:
async function ansyncExample() {
try {
// 3 async operations which depend on each other
const firstValue = await firstAsyncFunc();
const SecondValue = await secondAsyncFunc(firstValue);
const ThirdValue = await thirdAsyncFunc(SecondValue);
} catch (error) {
console.log(error);
}
return ThirdValue;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With