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.funccan't use the fulfillment value ofsubfunc's promise (though your code usingawaitdoesn'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
subfuncwithoutawait,something()is awaited anyway no?
That's a very good observation. Yes, something() is awaited 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.)
funccallssubfunc:subfunccallssomethingand gets back a value, let's call itpSomething. (Let's assume the normal case:pSomethingis a promise, and it's still pending.¹)subfuncusesawaitonpSomething:subfunccreates a promise, let's call itpSubfunc.awaitattaches fulfilment and rejection handlers topSomething. (More on what these handlers do later.)subfuncreturnspSubfunc.
- Now that
subfunchas returned,funcreturns, since it's not doing anything withsubfunc's promise:funccreates a promise (let's call itpFunc).funcfulfillspFuncwithundefined.funcreturnspFunc.
- It's possible nothing further happens because
pSomethingis never settled, but let's assume at some point it gets fulfilled:pSomething's settlement calls the fulfilment handler attached insubfuncin Step 1.2.2. That fulfilment handler contains the (implicit) code following theawaitinsubfunc, which isreturn;. That code fulfillspSubfuncwithundefined.
So as you can see, you're right that pSomething is awaited 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.
