How Async/Await actually works in javascript
When ever I talk to someone about “async/await”, many of them says that “async” is used for running our function asynchronously, and “await” is used for making our function wait at some point in our function.
But this is not entirely true. In fact, it makes our perception wrong about async/await and we end up using async/await incorrectly. So now the question is, what async/await actually is and how should we use it in our code.
Before we start to understand how async/await work, we should understand how promise works. Every body knows that “promises” are used to perform asynchronous tasks. Let us talk about in a little bit more detail. We will understand promises by implementing some code and guessing the output of our code. This is the best approach to understand any concept in any programming language.
console.log('top line of code');
let firstPromise = new Promise((resolve, reject) => {
console.log('before resolve');
resolve('resolved value');
console.log('after resolve');
});
console.log('after promise');
firstPromise.then(console.log);
console.log('after then');
Output 1
top line of code
before resolve
after resolve
after promise
after then
resolved value
By analysing output we can see that resolved value is printed at the end. This happened because line firstPromise.then(console.log)
executed asynchronously, and rest of the code executed synchronously, even the console.log('before resolve')
and console.log('after resolve')
.
This happened because the promises are “eager” in nature. They are executed as soon as they created. Only code inside “then” function get executed asynchronously.
Now we will observe below code
console.log('top line of code');let firstPromise = new Promise((resolve, reject) => {
console.log('first promise, before resolve.');
resolve('resolved value of first promise');
console.log('first promise, after resolved');
});console.log('after first promise');let secondPromise = new Promise((resolve, reject) => {
console.log('second promise, before resolve.');
firstPromise.then(console.log);
console.log('after then of first promise, inside secondPromise');
resolve('resolved value of second promise');
console.log('second promise, after resolved');
});secondPromise.then(console.log)
console.log('after then of second promise');
Output 2
top line of code
first promise, before resolve.
first promise, after resolved
after first promise
second promise, before resolve.
after then of first promise, inside secondPromise
second promise, after resolved
after then of second promise
resolved value of first promise
resolved value of second promise
Here I have introduced a second promise, which runs “then” then function of first promise. Both of the .then(console.log)
are executed asynchronously, no matter where they are written in our code. Now I will few lines from the above code and analyse a problem which may occur.
let secondPromise = new Promise((resolve, reject) => {
console.log('second promise, before resolve.');
firstPromise.then(console.log);
console.log('after then of first promise, inside secondPromise');
resolve('resolved value of second promise');
console.log('second promise, after resolved');
});
If we want resolve our secondPromise
based on the value resolved by firstPromise
, we can not do that with our current structure of code. It means that if we run
console.log('after then of first promise, inside secondPromise');
resolve('resolved value of second promise');
console.log('second promise, after resolved');
after the firstPromise.then(console.log)
we can not do that here. For that we have write our code as below
console.log('top line of code');
let firstPromise = new Promise((resolve, reject) => {
console.log('first promise, before resolve.');
resolve('resolved value of first promise');
console.log('first promise, after resolved');
});console.log('after first promise');
firstPromise.then(val => {
console.log(val);
let secondPromise = new Promise((resolve, reject) => {
console.log('second promise, before resolve.');
console.log('after then of firstpromise, inside secondPromise');
resolve('resolved value of second promise');
console.log('second promise, after resolved');
});
secondPromise.then(console.log);
});
console.log('after then of second promise');
Output 3
top line of code
first promise, before resolve.
first promise, after resolved
after first promise
after then of second promise
resolved value of first promise
second promise, before resolve.
after then of firstpromise, inside secondPromise
second promise, after resolved
resolved value of second promise
As you can see, we have to write our secondPromise
inside .then()
function of firstPromise. Suppose we have 10 promises, which we have to manage written in above order, in this case our code will become like callback hell
and it will be hard to manage.
To rescue us from above situation, we have async/await.The above scenario is called, running our async code in sync way.Async/Await helps us with the above scenario.
Finally we are ready to under stand Async/Await
Before writing code with async/await let us remember few point:
- async is written before the function name.
- async wraps returned value of a function into a resolved promise.
- await is used inside the function body.
- await cannot exist without async, but async can.
- await call “then” function of the a promise, and returns the resolved value.
- By nature await can be called on
thenable
objects, who has a then function. - all the code after “await” will be executed asynchronously , more precisely, our code will be added to the micro task queue for execution.
- By using async/await together, we can run our asynchronous code in a synchronous manner.
Let’s observe below code
async function haveFun() {
//function body
let val = 'abc';
return val;
}
console.log(haveFun());
Output 4
Promise {<pending>}
In the output we can see the async function returns a promise. Now we will write a code using both async and await.
async function haveFun() {
console.log('start of haveFun function');
let val = 'abc';
return val;
}async function haveMoreFun() {
console.log('start of haveMoreFun function');
console.log('calling haveFun() function');
let newVal = await haveFun();
console.log('after calling haveFun() with await');
return newVal + ' with more fun';
}haveMoreFun().then(console.log);
Output 5
start of haveMoreFun function
calling haveFun() function
start of haveFun function
after calling haveFun() with await
abc with more fun
Here you can see that in haveMoreFun()
function we are calling haveFun()
function, prefixing await
. The code after await will not be executed until haveFun() is completed. If we have been doing this with promise, our code will note have been paused at await and it will have been continued to execute. I hope by now we ready to rewrite out code, which we have written by using promise, with async and await. Let’s do it now.
With out async/wait our code will look like this.
console.log('top line of code');
let firstPromise = new Promise((resolve, reject) => {
console.log('first promise, before resolve.');
resolve('resolved value of first promise');
console.log('first promise, after resolved');
});console.log('after first promise');
firstPromise.then(val => {
console.log(val);
let secondPromise = new Promise((resolve, reject) => {
console.log('second promise, before resolve.');
console.log('after then of firstpromise, inside secondPromise');
resolve('resolved value of second promise');
console.log('second promise, after resolved');
});
secondPromise.then(console.log);
});
console.log('after then of second promise');
With async/await, our code will look like this.
console.log('top line of code');
let firstPromise = new Promise((resolve, reject) => {
console.log('first promise, before resolve.');
resolve('resolved value of first promise');
});console.log('first promise, after resolved');
console.log('after first promise');async function secondPromise() {
let val = await firstPromise;
console.log(val);
console.log('second promise, before resolve.');
console.log('after then of firstpromise, inside secondPromise');
return 'resolved value of second promise';
}console.log('second promise, after resolved');
secondPromise().then(console.log);
console.log('after then of second promise');
Output 6
top line of code
first promise, before resolve.
first promise, after resolved
after first promise
second promise, after resolved
after then of second promise
resolved value of first promise
second promise, before resolve.
after then of firstpromise, inside secondPromise
resolved value of second promise
Here we can see the, our code is inside function secondPromise
is executed after the firstpromise
code. If we consider a scenario where we have 10 promise and output of second depends on the output of one , output of third depends on output of second, we can rewrite out code by using async function with await in side function body.
However the code which we have written inline will be executed as synchronous code. In real life scenario we hardly writes any inline code. We always writes our code inside a function and returns a value.
With async function we have flexibility of passing parameters. This is not easy with promises. If someone wants to work with promise I will suggest to write their code with async/await instead of writing promises directly.
I hope by all of your questions related to async/await has been cleared. I have left one scenario by purpose and that is the case of reject promises
. This can be achieved by calling await
inside try catch
block. This exercise I left for the reader.
If you have any more question feel free to ask in comment.
Thanks.