What is the difference between:
async function subfunc(){
await something()
}
async function func(){
subfunc() // subfunc is not awaited but something inside it is
}
func()
and
async function subfunc(){
await something()
}
async function func(){
await subfunc() // subfunc is awaited and something inside it is also
}
func()
It sounds like it produces the same result, where am I wrong?
CodePudding user response:
It sounds like it produces the same result, where am I wrong?
The results are very different.
Without await
, the promise returned by subfunc
is not awaited, so func
settles its promise without waiting for the promise from subfunc
to settle. Several ramifications of that:
subfunc
's work won't be done yet whenfunc
's promise is settled.func
can't use the fulfillment value ofsubfunc
's promise (though your code usingawait
doesn't use it either, so that may not matter to you).- if
subfunc
's promise is rejected, it doesn't cause rejection offunc
's promise;func
's promise will already be fulfilled. In fact, nothing will handle the rejection ofsubfunc
's promise, which will at a minimum trigger a console warning about an unhandled rejction or at a maxiumum could terminate the process the code is running in (Node.js does this by default, for instance).
(Re that third one: It happens that in the example you've given, nothing is handling rejection of func
's promise either, but at least code calling func
has the opportunity to handle rejection. Without the await
of subfunc
's promise, code calling func
can't avoid a possible unhandled rejection.)
You can see some of that in these two examples (be sure to look int he real browser console):
Without await
:
function something(flag) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (flag) {
console.log("`subfunc` promise fulfilled");
resolve();
} else {
console.log("`subfunc` promise rejected");
reject(new Error("Some error here"));
}
}, 100);
});
}
async function subfunc(flag) {
await something(flag);
}
async function func(flag) {
subfunc(flag); // No `await`
}
async function test(flag) {
console.log("-");
console.log(
`Calling \`func\` to test ${flag ? "fulfillment" : "rejection"}:`
);
try {
await func(flag);
console.log("`func` promise fulfilled");
} catch (error) {
console.log("`func` promise rejected: ", error.message);
}
}
console.log("WITHOUT `await`:");
test(true).then(() => {
setTimeout(() => {
test(false);
}, 500);
});
.as-console-wrapper {
max-height: 100% !important;
}
Notice the
Uncaught (in promise) Error: Some error here
or similar in the real browser console. That's the unhandled rejection.
With await
:
function something(flag) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (flag) {
console.log("`subfunc` promise fulfilled");
resolve();
} else {
console.log("`subfunc` promise rejected");
reject(new Error("Some error here"));
}
}, 100);
});
}
async function subfunc(flag) {
await something(flag);
}
async function func(flag) {
await subfunc(flag);
}
async function test(flag) {
console.log("-");
console.log(
`Calling \`func\` to test ${flag ? "fulfillment" : "rejection"}:`
);
try {
await func(flag);
console.log("`func` promise fulfilled");
} catch (error) {
console.log("`func` promise rejected: ", error.message);
}
}
console.log("WITH `await`:");
test(true).then(() => {
setTimeout(() => {
test(false);
}, 500);
});
.as-console-wrapper {
max-height: 100% !important;
}
In a comment you asked:
this is what I don't get, we you execute
subfunc
withoutawait
,something()
is awaited anyway no?
That's a very good observation. Yes, something()
is await
ed in subfunc
(so subfunc
doesn't settle its promise until the promise it gets from something()
settles), but that doesn't make func
wait for subfunc
to settle its promise.
It may help to walk through the execution of your first code block (the one without await
in func
), starting where the main script calls func
. (Please note that I've omitted some details for clarity.)
func
callssubfunc
:subfunc
callssomething
and gets back a value, let's call itpSomething
. (Let's assume the normal case:pSomething
is a promise, and it's still pending.¹)subfunc
usesawait
onpSomething
:subfunc
creates a promise, let's call itpSubfunc
.await
attaches fulfilment and rejection handlers topSomething
. (More on what these handlers do later.)subfunc
returnspSubfunc
.
- Now that
subfunc
has returned,func
returns, since it's not doing anything withsubfunc
's promise:func
creates a promise (let's call itpFunc
).func
fulfillspFunc
withundefined
.func
returnspFunc
.
- It's possible nothing further happens because
pSomething
is never settled, but let's assume at some point it gets fulfilled:pSomething
's settlement calls the fulfilment handler attached insubfunc
in Step 1.2.2. That fulfilment handler contains the (implicit) code following theawait
insubfunc
, which isreturn;
. That code fulfillspSubfunc
withundefined
.
So as you can see, you're right that pSomething
is await
ed by subfunc
, and so pSubfunc
doesn't settle until pSomething
settles. But func
doesn't await
pSubfunc
, so func
fulfills pFunc
as soon as subfunc
returns its still-pending promise.
And that's the crucial difference in the code: func
doesn't wait for subfunc
to finish its work, even though subfunc
waits for something
to finish its work.
¹ If pSomething
weren't a promise, await
would create and fulfill a new promise using the non-promise value it received as the fulfilment value and use that instead, which would change a couple of minor details in the explanation above, but not in ways that your code could easily observe.