Why doesn't the code after await run right away? Isn't it supposed to be non-blocking?
Just as its name implies, the await
keyword will cause the function to "wait" until its promise resolves before executing the next line. The whole point of await
is to get the code to wait until the operation is done before continuing.
The difference between this and blocking code is that the world outside the function can continue executing while the function is waiting for the asynchronous operations to finish.
async
and await
are just syntactic sugar on top of promises. They allow you to write code that looks a lot like ordinary synchronous code even though it uses promises under the covers. If we translated your example there to something that explicitly worked with the promises, it would look something like:
function myAsyncFunction() {
return myAPICall('https://jsonplaceholder.typicode.com/posts/1')
.then(function (data) {
let result = 2 + 2;
return data;
})
.catch(function (ex) {
return ex;
});
}
As we can see here, the let result = 2 + 2;
line is inside a .then()
handler, which means it's not going to execute until myAPICall()
has resolved. It's the same when you use await
. await
just abstracts away the .then()
for you.
One thing to bear in mind (and I think the point you're looking for) is that you don't have to use await
right away. If you wrote your function like this, then you could execute your let result = 2 + 2;
line right away:
const timeout =
seconds => new Promise(res => setTimeout(res, seconds * 1000));
function myAPICall() {
// simulate 1 second wait time
return timeout(1).then(() => 'success');
}
async function myAsyncFunction() {
try {
console.log('starting');
// just starting the API call and storing the promise for now. not waiting yet
let dataP = myAPICall('https://jsonplaceholder.typicode.com/posts/1');
let result = 2 + 2;
// Executes right away
console.log('result', result);
// wait now
let data = await dataP;
// Executes after one second
console.log('data', data);
return data;
} catch (ex) {
return ex;
}
}
myAsyncFunction();
After some clarification, I can see that what you really wanted to know about is how to avoid having to wait for two async operations one by one and instead have them execute in parallel. Indeed, if you use one await
after the other, the second won't start executing until the first has finished:
const timeout =
seconds => new Promise(res => setTimeout(res, seconds * 1000));
function myAPICall() {
// simulate 1 second wait time
return timeout(1).then(() => 'success');
}
async function myAsyncFunction() {
try {
console.log('starting');
let data1 = await myAPICall('https://jsonplaceholder.typicode.com/posts/1');
// logs after one second
console.log('data1', data1);
let data2 = await myAPICall('https://jsonplaceholder.typicode.com/posts/2');
// logs after one more second
console.log('data2', data2);
} catch (ex) {
return ex;
}
}
myAsyncFunction();
To avoid this, what you can do is start both async operations by executing them without awaiting them, assigning their promises to some variables. Then you can await both promises:
const timeout =
seconds => new Promise(res => setTimeout(res, seconds * 1000));
function myAPICall() {
// simulate 1 second wait time
return timeout(1).then(() => 'success');
}
async function myAsyncFunction() {
try {
console.log('starting');
// both lines execute right away
let dataP1 = myAPICall('https://jsonplaceholder.typicode.com/posts/1');
let dataP2 = myAPICall('https://jsonplaceholder.typicode.com/posts/2');
let data1 = await dataP1;
let data2 = await dataP2;
// logs after one second
console.log('data1', data1);
console.log('data2', data2);
} catch (ex) {
return ex;
}
}
myAsyncFunction();
One alternative way to do this is to use Promise.all()
with some array decomposition:
const timeout =
seconds => new Promise(res => setTimeout(res, seconds * 1000));
function myAPICall() {
// simulate 1 second wait time
return timeout(1).then(() => 'success');
}
async function myAsyncFunction() {
try {
console.log('starting');
// both myAPICall invocations execute right away
const [data1, data2] = await Promise.all([
myAPICall('https://jsonplaceholder.typicode.com/posts/1'),
myAPICall('https://jsonplaceholder.typicode.com/posts/2'),
]);
// logs after one second
console.log('data1', data1);
console.log('data2', data2);
} catch (ex) {
return ex;
}
}
myAsyncFunction();
Rohail Najam
Accomplished Software Engineer with years of successfully developing software and systems for high-traffic social networking services.
Updated on December 25, 2020Comments
-
Rohail Najam over 3 years
I have a hard time understanding how async and await works behind the scenes. I know we have promises which make our non blocking code by using the "then" function we can place all the work we need to do after the promise is resolved. and the work we want to do parallel to promise we just write it outside our then function. Hence the code becomes non blocking. However i don't understand how the
async
await
makes non-blocking code.async function myAsyncFunction() { try { let data = await myAPICall('https://jsonplaceholder.typicode.com/posts/1'); // It will not run this line until it resolves await. let result = 2 + 2; return data; }catch (ex){ return ex; } }
See the above code. I cannot move forward until the API call is resolved. If it makes my code blocking code, how is it any better then promises? Or is there something I missed about
async
andawait
? Where do i put my code that is not dependent to the await call? so it can keep on working without await holding the execution?I am adding a Promise code that i would like to replicate in an async await example.
function myPromiseAPI() { myAPICall('https://jsonplaceholder.typicode.com/posts/1') .then(function (data) { // data }); // runs parallel let result = 2 + 2; }
-
Rohail Najam about 7 yearsI understand this. what my point is that if we want to run anything in parallel to this api call we will just put it next to the then function like i did in my edited question. How will i achieve this "Parallelism" in async await
-
JLRishe about 7 years@RohailNajam Please see my update. You can "start" as many promises in parallel as you want. Once you use
await
, the function will start waiting on their result. -
Rohail Najam about 7 yearsoh i did not knew that. Thanks.
-
Rohail Najam about 7 years
let results = await Promise.all([dataP1, dataP2]);
will return an array that would contain results from both data.? -
JLRishe about 7 years@RohailNajam Yes, that's correct. I've modified my answer to show how you can use array decomposition to split it into its respective values.
-
Rohail Najam about 7 yearsThanks for the help
-
a better oliver about 7 years"the world outside the function will continue to go on executing while this function is doing its thing" The "world outside" will go on while the function is not doing anything.
-
JLRishe over 6 years@zeroflagL A nitpick, indeed, but fair enough. I've fixed the wording.
-
Jeremy F over 2 yearsI think the phrase "The difference between this and blocking code is that the world outside the function can continue executing while the function is waiting for the asynchronous operations to finish." is for me a point of confusion. What exactly keeps running? Does the caller of the async method continue on with the lines of code after the call to the async method that contains an "await" ?
-
JLRishe about 2 years@JeremyF If the caller of the function uses
await
or.then
, then it likewise would wait until this function completed. But if it called the function without awaiting ("fire and forget"), then yes, it would continue executing. But perhaps more importantly, the "world outside" includes event handlers,setTimeout
, etc. that can run even if something is beingawait
ed. They cannot run if there is blocking, non-async code holding up the works.