Why await only works in async function in javascript? - javascript

Just going through this tutorial, and it baffles me to understand why await only works in async function.
From the tutorial:
As said, await only works inside async function.
From my understanding, async wraps the function return object into a Promise, so the caller can use .then()
async function f() {
return 1;
}
f().then(alert); // 1
And await just waits for the promise to settle within the async function.
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // wait till the promise resolves (*)
alert(result); // "done!"
}
f();
It seems to me their usage are not related, could someone please explain?

Code becomes asynchronous on await - we wouldn't know what to return
What await does in addition to waiting for the promise to resolve is that it immediately returns the code execution to the caller. All code inside the function after await is asynchronous.
async is syntatic sugar for returning a promise.
If you don't want to return a promise at await, what would be the sane alternative in an asynchronous code?
Let's look at the following erroneous code to see the problem of the return value:
function f() {
// Execution becomes asynchronous after the next line, what do we want to return to the caller?
let result = await myPromise;
// No point returning string in async code since the caller has already moved forward.
return "function finished";
}
We could instead ask another question: why don't we have a synchronous version of await that wouldn't change the code to asynchronous?
My take on that is that for many good reasons making asynchronous code synchronous has been made difficult by design. For example, it would make it too easy for people to accidentally make their whole application to freeze when waiting for an asynchronous function to return.
To further illustrate the runtime order with async and await:
async function f() {
for(var i = 0; i < 1000000; i++); // create some synchronous delay
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
console.log("message inside f before returning, still synchronous, i = " + i);
// let's await and at the same time return the promise to the caller
let result = await promise;
console.log("message inside f after await, asynchronous now");
console.log(result); // "done!"
return "function finished";
}
let myresult = f();
console.log("message outside f, immediately after calling f");
The console log output is:
message inside f before returning, still synchronous, i = 1000000
message message outside f, immediately after calling f
message inside f after await, asynchronous now
done!

async and await are both meta keywords that allow asynchronous code to be written in a way that looks synchronous. An async function tells the compiler ahead of time that the function will be returning a Promise and will not have a value resolved right away. To use await and not block the thread async must be used.
async function f() {
return await fetch('/api/endpoint');
}
is equivalent to
function f() {
return new Promise((resolve,reject) => {
return fetch('/api/endpoint')
.then(resolve);
});
}

Related

Should javascript async function be called with await keyword? [duplicate]

This question already has answers here:
Does an async function always need to be called with await?
(4 answers)
Closed 10 months ago.
I have an async function calling some asynchronous code:
async function foo(test){
const value = await call(test)
}
I then call this function from another file without:
foo(5)
The linter is throwing a warning saying Missing await for an async function call. This does not make sense to me.
The asynchronous code inside the function is called with await keyword which makes foo run sequentially when called right?
Why is this warning being thrown?
Should javascript async function be called with await keyword?
That depends. If you want the logic where the code is calling foo() to wait until foo has completed its asynchronous work, then yes, you should use await.
If you don't want the logic at the call site to wait for foo to finish its work, don't use await, but do store the promise it provides and await (or otherwise use it) at some point. Otherwise, you won't handle it if the promise is rejected, and not handling promise rejections is a source of bugs.
So either (in an async function):
await foo();
or (in a non-async function):
return foo(); // Let the caller handle the promise
or (in a non-async function where you can't return the promise to your caller):
foo()
.then(value => {
// ...use value (except your `foo` doesn't return one,
// so really this is just "foo worked")...
})
.catch(error => {
// ...handle/report error...
});
etc.
Fundamentally, though: An async function returns a promise, so you should do something to handle that promise (by passing it back to your caller, or handling it locally, etc.).
Let's look at what happens in an async function calling foo when call fails, both with and without await:
With await:
async function call(test) {
if (test === undefined) {
throw new Error("'test' is required");
}
// Stand-in for something asynchrojous using `test`
const result = await new Promise(resolve => {
setTimeout(() => {
resolve(test * 2);
}, 10);
});
return result;
}
async function foo(test) {
const value = await call(test)
console.log(`foo got value: ${value}`);
}
async function example() {
foo();
}
example()
.then(() => {
console.log("All done, no errors");
})
.catch((error) => {
console.error("Error: " + error);
});
When you run that, it says "All done, no errors" but if you look in the devtools console, you'll see "Uncaught (in promise) Error: 'test' is required" (an unhandled rejection).
Compare with using await:
async function call(test) {
if (test === undefined) {
throw new Error("'test' is required");
}
// Stand-in for something asynchrojous using `test`
const result = await new Promise(resolve => {
setTimeout(() => {
resolve(test * 2);
}, 10);
});
return result;
}
async function foo(test) {
const value = await call(test)
console.log(`foo got value: ${value}`);
}
async function example() {
await foo();
}
example()
.then(() => {
console.log("All done, no errors");
})
.catch((error) => {
console.error(error);
});
As you can see, the error from call was caught (it propagated from call to foo, then from foo to example, then from example to where we call it and handle errors).
When you declare a function async it will always return a Promise. When a function returns a Promise you should treat it as if it is asynchronous, and you should figure out if any function that calls this function marked with async should wait for that function to complete, or explicitly allow it to run asynchronously if it needs to.
You either need to await your call to foo (which would make whatever function you are calling it in also an async function), or use .then(..) to execute your code after your call to foo succesfully completes. Alternatively if you want the code to execute asynchronously you can explicitly declare this using the syntax of your linter right above the call to foo(5).

Why try catch is ignored when returning an async function with no await prefix [duplicate]

Given the code samples below, is there any difference in behavior, and, if so, what are those differences?
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
async function delay1Second() {
return delay(1000);
}
As I understand it, the first would have error-handling within the async function, and errors would bubble out of the async function's Promise. However, the second would require one less tick. Is this correct?
This snippet is just a common function to return a Promise for reference.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
Most of the time, there is no observable difference between return and return await. Both versions of delay1Second have the exact same observable behavior (but depending on the implementation, the return await version might use slightly more memory because an intermediate Promise object might be created).
However, as #PitaJ pointed out, there is one case where there is a difference: if the return or return await is nested in a try-catch block. Consider this example
async function rejectionWithReturnAwait () {
try {
return await Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
async function rejectionWithReturn () {
try {
return Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
In the first version, the async function awaits the rejected promise before returning its result, which causes the rejection to be turned into an exception and the catch clause to be reached; the function will thus return a promise resolving to the string "Saved!".
The second version of the function, however, does return the rejected promise directly without awaiting it within the async function, which means that the catch case is not called and the caller gets the rejection instead.
As other answers mentioned, there is likely a slight performance benefit when letting the promise bubble up by returning it directly — simply because you don’t have to await the result first and then wrap it with another promise again. However, no one has talked about tail call optimization yet.
Tail call optimization, or “proper tail calls”, is a technique that the interpreter uses to optimize the call stack. Currently, not many runtimes support it yet — even though it’s technically part of the ES6 Standard — but it’s possible support might be added in the future, so you can prepare for that by writing good code in the present.
In a nutshell, TCO (or PTC) optimizes the call stack by not opening a new frame for a function that is directly returned by another function. Instead, it reuses the same frame.
async function delay1Second() {
return delay(1000);
}
Since delay() is directly returned by delay1Second(), runtimes supporting PTC will first open a frame for delay1Second() (the outer function), but then instead of opening another frame for delay() (the inner function), it will just reuse the same frame that was opened for the outer function. This optimizes the stack because it can prevent a stack overflow (hehe) with very large recursive functions, e.g., fibonacci(5e+25). Essentially it becomes a loop, which is much faster.
PTC is only enabled when the inner function is directly returned. It’s not used when the result of the function is altered before it is returned, for example, if you had return (delay(1000) || null), or return await delay(1000).
But like I said, most runtimes and browsers don’t support PTC yet, so it probably doesn’t make a huge difference now, but it couldn’t hurt to future-proof your code.
Read more in this question: Node.js: Are there optimizations for tail calls in async functions?
Noticeable difference: Promise rejection gets handled at different places
return somePromise will pass somePromise to the call site, and await somePromise to settle at call site (if there is any). Therefore, if somePromise is rejected, it will not be handled by the local catch block, but the call site's catch block.
async function foo () {
try {
return Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'OUT'
return await somePromise will first await somePromise to settle locally. Therefore, the value or Exception will first be handled locally. => Local catch block will be executed if somePromise is rejected.
async function foo () {
try {
return await Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'IN'
Reason: return await Promise awaits both locally and outside, return Promise awaits only outside
Detailed Steps:
return Promise
async function delay1Second() {
return delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Async functions will wrap their return value inside Promise.resolve()(Source). Because delay1Second is an async function, we have:
const result = await Promise.resolve(delayPromise);
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Promise.resolve(delayPromise) returns delayPromise without doing anything because the input is already a promise (see MDN Promise.resolve):
const result = await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
await waits until the delayPromise is settled.
IF delayPromise is fulfilled with PromiseValue=1:
const result = 1;
ELSE is delayPromise is rejected:
// jump to catch block if there is any
return await Promise
async function delay1Second() {
return await delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Local await will wait until delayPromise gets settled.
Case 1: delayPromise is fulfilled with PromiseValue=1:
async function delay1Second() {
return 1;
}
const result = await Promise.resolve(1); // let's call it "newPromise"
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
const result = 1;
Case 2: delayPromise is rejected:
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
const result = await Promise.resolve(-1); // call it newPromise
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
const result = -1;
Glossary:
Settle: Promise.[[PromiseStatus]] changes from pending to resolved or rejected
This is a hard question to answer, because it depends in practice on how your transpiler (probably babel) actually renders async/await. The things that are clear regardless:
Both implementations should behave the same, though the first implementation may have one less Promise in the chain.
Especially if you drop the unnecessary await, the second version would not require any extra code from the transpiler, while the first one does.
So from a code performance and debugging perspective, the second version is preferable, though only very slightly so, while the first version has a slight legibility benefit, in that it clearly indicates that it returns a promise.
In our project, we decided to always use 'return await'.
The argument is that "the risk of forgetting to add the 'await' when later on a try-catch block is put around the return expression justifies having the redundant 'await' now."
Here is a typescript example that you can run and convince yourself that you need that "return await"
async function test() {
try {
return await throwErr(); // this is correct
// return throwErr(); // this will prevent inner catch to ever to be reached
}
catch (err) {
console.log("inner catch is reached")
return
}
}
const throwErr = async () => {
throw("Fake error")
}
void test().then(() => {
console.log("done")
}).catch(e => {
console.log("outer catch is reached")
});
here i leave some code practical for you can undertand it the diferrence
let x = async function () {
return new Promise((res, rej) => {
setTimeout(async function () {
console.log("finished 1");
return await new Promise((resolve, reject) => { // delete the return and you will see the difference
setTimeout(function () {
resolve("woo2");
console.log("finished 2");
}, 5000);
});
res("woo1");
}, 3000);
});
};
(async function () {
var counter = 0;
const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
if (counter == 7) {
clearInterval(a);
}
console.log(counter);
counter = counter + 1;
}, 1000);
console.time("time1");
console.log("hello i starting first of all");
await x();
console.log("more code...");
console.timeEnd("time1");
})();
the function "x" just is a function async than it have other fucn
if will delete the return it print "more code..."
the variable x is just an asynchronous function that in turn has another asynchronous function, in the main of the code we invoke a wait to call the function of the variable x, when it completes it follows the sequence of the code, that would be normal for "async / await ", but inside the x function there is another asynchronous function, and this returns a promise or returns a" promise "it will stay inside the x function, forgetting the main code, that is, it will not print the" console.log ("more code .. "), on the other hand if we put" await "it will wait for every function that completes and finally follows the normal sequence of the main code.
below the "console.log (" finished 1 "delete the" return ", you will see the behavior.

Async / await invocations [duplicate]

Given the code samples below, is there any difference in behavior, and, if so, what are those differences?
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
async function delay1Second() {
return delay(1000);
}
As I understand it, the first would have error-handling within the async function, and errors would bubble out of the async function's Promise. However, the second would require one less tick. Is this correct?
This snippet is just a common function to return a Promise for reference.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
Most of the time, there is no observable difference between return and return await. Both versions of delay1Second have the exact same observable behavior (but depending on the implementation, the return await version might use slightly more memory because an intermediate Promise object might be created).
However, as #PitaJ pointed out, there is one case where there is a difference: if the return or return await is nested in a try-catch block. Consider this example
async function rejectionWithReturnAwait () {
try {
return await Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
async function rejectionWithReturn () {
try {
return Promise.reject(new Error())
} catch (e) {
return 'Saved!'
}
}
In the first version, the async function awaits the rejected promise before returning its result, which causes the rejection to be turned into an exception and the catch clause to be reached; the function will thus return a promise resolving to the string "Saved!".
The second version of the function, however, does return the rejected promise directly without awaiting it within the async function, which means that the catch case is not called and the caller gets the rejection instead.
As other answers mentioned, there is likely a slight performance benefit when letting the promise bubble up by returning it directly — simply because you don’t have to await the result first and then wrap it with another promise again. However, no one has talked about tail call optimization yet.
Tail call optimization, or “proper tail calls”, is a technique that the interpreter uses to optimize the call stack. Currently, not many runtimes support it yet — even though it’s technically part of the ES6 Standard — but it’s possible support might be added in the future, so you can prepare for that by writing good code in the present.
In a nutshell, TCO (or PTC) optimizes the call stack by not opening a new frame for a function that is directly returned by another function. Instead, it reuses the same frame.
async function delay1Second() {
return delay(1000);
}
Since delay() is directly returned by delay1Second(), runtimes supporting PTC will first open a frame for delay1Second() (the outer function), but then instead of opening another frame for delay() (the inner function), it will just reuse the same frame that was opened for the outer function. This optimizes the stack because it can prevent a stack overflow (hehe) with very large recursive functions, e.g., fibonacci(5e+25). Essentially it becomes a loop, which is much faster.
PTC is only enabled when the inner function is directly returned. It’s not used when the result of the function is altered before it is returned, for example, if you had return (delay(1000) || null), or return await delay(1000).
But like I said, most runtimes and browsers don’t support PTC yet, so it probably doesn’t make a huge difference now, but it couldn’t hurt to future-proof your code.
Read more in this question: Node.js: Are there optimizations for tail calls in async functions?
Noticeable difference: Promise rejection gets handled at different places
return somePromise will pass somePromise to the call site, and await somePromise to settle at call site (if there is any). Therefore, if somePromise is rejected, it will not be handled by the local catch block, but the call site's catch block.
async function foo () {
try {
return Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'OUT'
return await somePromise will first await somePromise to settle locally. Therefore, the value or Exception will first be handled locally. => Local catch block will be executed if somePromise is rejected.
async function foo () {
try {
return await Promise.reject();
} catch (e) {
console.log('IN');
}
}
(async function main () {
try {
let a = await foo();
} catch (e) {
console.log('OUT');
}
})();
// 'IN'
Reason: return await Promise awaits both locally and outside, return Promise awaits only outside
Detailed Steps:
return Promise
async function delay1Second() {
return delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Async functions will wrap their return value inside Promise.resolve()(Source). Because delay1Second is an async function, we have:
const result = await Promise.resolve(delayPromise);
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
Promise.resolve(delayPromise) returns delayPromise without doing anything because the input is already a promise (see MDN Promise.resolve):
const result = await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
await waits until the delayPromise is settled.
IF delayPromise is fulfilled with PromiseValue=1:
const result = 1;
ELSE is delayPromise is rejected:
// jump to catch block if there is any
return await Promise
async function delay1Second() {
return await delay(1000);
}
call delay1Second();
const result = await delay1Second();
Inside delay1Second(), function delay(1000) returns a promise immediately with [[PromiseStatus]]: 'pending. Let's call it delayPromise.
async function delay1Second() {
return await delayPromise;
// delayPromise.[[PromiseStatus]]: 'pending'
// delayPromise.[[PromiseValue]]: undefined
}
Local await will wait until delayPromise gets settled.
Case 1: delayPromise is fulfilled with PromiseValue=1:
async function delay1Second() {
return 1;
}
const result = await Promise.resolve(1); // let's call it "newPromise"
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: 1
const result = 1;
Case 2: delayPromise is rejected:
// jump to catch block inside `delay1Second` if there is any
// let's say a value -1 is returned in the end
const result = await Promise.resolve(-1); // call it newPromise
const result = await newPromise;
// newPromise.[[PromiseStatus]]: 'resolved'
// newPromise.[[PromiseValue]]: -1
const result = -1;
Glossary:
Settle: Promise.[[PromiseStatus]] changes from pending to resolved or rejected
This is a hard question to answer, because it depends in practice on how your transpiler (probably babel) actually renders async/await. The things that are clear regardless:
Both implementations should behave the same, though the first implementation may have one less Promise in the chain.
Especially if you drop the unnecessary await, the second version would not require any extra code from the transpiler, while the first one does.
So from a code performance and debugging perspective, the second version is preferable, though only very slightly so, while the first version has a slight legibility benefit, in that it clearly indicates that it returns a promise.
In our project, we decided to always use 'return await'.
The argument is that "the risk of forgetting to add the 'await' when later on a try-catch block is put around the return expression justifies having the redundant 'await' now."
Here is a typescript example that you can run and convince yourself that you need that "return await"
async function test() {
try {
return await throwErr(); // this is correct
// return throwErr(); // this will prevent inner catch to ever to be reached
}
catch (err) {
console.log("inner catch is reached")
return
}
}
const throwErr = async () => {
throw("Fake error")
}
void test().then(() => {
console.log("done")
}).catch(e => {
console.log("outer catch is reached")
});
here i leave some code practical for you can undertand it the diferrence
let x = async function () {
return new Promise((res, rej) => {
setTimeout(async function () {
console.log("finished 1");
return await new Promise((resolve, reject) => { // delete the return and you will see the difference
setTimeout(function () {
resolve("woo2");
console.log("finished 2");
}, 5000);
});
res("woo1");
}, 3000);
});
};
(async function () {
var counter = 0;
const a = setInterval(function () { // counter for every second, this is just to see the precision and understand the code
if (counter == 7) {
clearInterval(a);
}
console.log(counter);
counter = counter + 1;
}, 1000);
console.time("time1");
console.log("hello i starting first of all");
await x();
console.log("more code...");
console.timeEnd("time1");
})();
the function "x" just is a function async than it have other fucn
if will delete the return it print "more code..."
the variable x is just an asynchronous function that in turn has another asynchronous function, in the main of the code we invoke a wait to call the function of the variable x, when it completes it follows the sequence of the code, that would be normal for "async / await ", but inside the x function there is another asynchronous function, and this returns a promise or returns a" promise "it will stay inside the x function, forgetting the main code, that is, it will not print the" console.log ("more code .. "), on the other hand if we put" await "it will wait for every function that completes and finally follows the normal sequence of the main code.
below the "console.log (" finished 1 "delete the" return ", you will see the behavior.

What exactly happens when awaiting a promise in JavaScript?

One point that I can’t seem to find anywhere in documentation or related resources is what the procedure is, in regards to the calling function and the event loop, when JS hits the await keyword.
Here’s what the specs state:
The await expression causes async function execution to pause until a Promise is resolved, that is fulfilled or rejected, and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise.
That all makes sense, but what really is meant by pause? Does the function immediately return to the caller and then when the promise that’s being awaited resolves, it and the rest of the async method gets added to the microtask queue for handling? I know that async/await is syntactic sugar for the Promise API, so I’m assuming that this is sorta what happens, but I want to make sure I’m really understanding the “magic” that happens when an await is reached.
From what I’ve read, this seems to be what’s done in C#, but I’m not exactly sure how it translates to JS.
It is exactly as you said, it puts the rest of the function into a micro task Q, and returns the main function.
Then when the promise resolves, it executes the async part.
What helps me to understand it is to convert the async/await into promise syntax, and then it gets clear.
For example:
async function doSomthing() {
someSync();
const result1 = await someAsync();
const result2 = await someAsync2();
return result1 + result2;
}
"converts" to:
function doSomthing() {
someSync();
return someAsync().then(result1 => {
return someAsync2().then(result2 => {
return result1 + result2;
});
});
}
You can see that doSomthing immediately returns the promise.
await/async means that we execute the code synchronously. It will follow the order of the await function is called within async function.
for example: It will process the execution line by line like it waits it the response is returned from await and then it proceeds further.
`function doubleAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x * 2);
}, 2000);
});
}
async function addAsync(x) {
console.log("Here I am before first await");
const a = await doubleAfter2Seconds(10);
console.log("A ouput is ",a);
const b = await doubleAfter2Seconds(20);
console.log("B ouput is ",b);
const c = await doubleAfter2Seconds(30);
console.log("C ouput is ",c);
return x + a + b + c;
}
addAsync(10).then((sum) => {
console.log(sum);
});`
The above code will output the result like below:
Hope this helps to understand async/await

why Javascript async/await code run async in parellel

I saw this good article to introduce async/await from Google.
However, I couldn't understand why these code run in parallel
async function parallel() {
const wait1 = wait(500);
const wait2 = wait(500);
await wait1;
await wait2;
return "done!";
}
And this run in series
async function series() {
await wait(500);
await wait(500);
return "done!";
}
Why is the key difference between these two methods ?
In my opinion, both of them are await promise and should work the same result.
Here is my test code. Can run in browser console which support async/await.
function wait(){
return new Promise((res)=>{setTimeout(()=>{res()}, 2000)})
}
async function parallel() {
const wait1 = wait();
const wait2 = wait();
await wait1;
await wait2;
return "done!";
}
async function series() {
await wait();
await wait();
return "done!";
}
parallel().then(res => console.log("parallel!"))
series().then(res => console.log("series!"))
======
Thanks for the answers.
But I still have some question. Why exact the async/await means?
In my knowledge, constructing Promise instance would execute directly.
Here is my test code
function wait(){
return new Promise((res)=>{setTimeout(()=>{console.log("wait!");res();}, 2000)})
}
wait()
//Promise {<pending>}
//wait!
let w = wait()
//undefined
//wait!
let w = await wait()
//wait!
//undefined
async function test(){await wait()}
// undefined
test()
//Promise {<pending>}
//wait!
So why const wait1 = wait(); inside parallel function execute directly?
By the way, should I open another question to ask these question?
await doesn't cause the Promise or its setTimeout() to start, which seems to be what you're expecting. The call to wait() alone starts them immediately, whether there's an active await on the promise or not.
await only helps you know when the already on-going operation, tracked through the promise, has completed.
So, the difference is just due to when wait() is being called and starting each timeout:
parallel() calls wait() back-to-back as quickly as the engine can get from one to the next, before either are awaited, so the 2 timeouts begin/end at nearly the same time.
series() forces the 2nd wait() to not be called until after the 1st has completed, by having an await act in between them.
Regarding your edit, async and await are syntactic sugar. Using them, the engine will modify your function at runtime, inserting additional code needed to interact with the promises.
A (possible, but not precise) equivalent of series() without await and async might be:
function series() {
return Promise.resolve()
.then(function () { return wait(500) })
.then(function () { return wait(500) })
.then(function () { return "done!"; });
}
And for parallel():
function parallel() {
const wait1 = wait(500);
const wait2 = wait(500);
return Promise.resolve()
.then(wait1)
.then(wait2)
.then(function () { return "done!"; });
}
In parallel(), you call both methods and then await their results while in series() you await the result of the first wait() call before calling the second wait().
Why exact the async/await means? In my knowledge, constructing Promise instance would execute directly.
The Promise instance is returned immediately, i.e. synchronously. But the value of the Promise is evaluated asynchronously by calling the first parameter given to its constructor, a function usually called resolve. This is what you are awaiting for.
The difference is that in parallel, both of the promises are scheduled right after each other.
In series, you wait for the first wait to resolve and then schedule the second wait to be called.
If we expand the methods we'd get something similar to:
function parallel() {
const wait1 = wait();
const wait2 = wait();
// Both wait1 and wait2 timeouts are scheduled
return wait1
.then(() => wait2)
.then(() => "done");
}
function series() {
// The second wait happens _after_ the first has been *resolved*
return wait()
.then(() => wait())
.then(() => "done");
}

Categories