I know that all functions marked with async
return a Promise object, even if it’s not returned explicitly. So I made the following experiment:
async function getPromise() {
setTimeout(() => { return "value"; }, 2000);
}
async function implicitPromiseTest() {
const val = await getPromise();
console.log(val); // prints "undefined"
}
implicitPromiseTest();
In example 1, the value after return
should be implicitly wrapped in a Promise. This Promise would then be returned from the function. I expected it to be resolved in the implicitPromiseTest
function and the returned value – printed to console.
I guess this doesn’t work because the implicit Promise from getPromise
never actually resolves. As I understand it, the proper way to do this task seems to be:
async function getPromise() {
return new Promise((resolve, reject) =>
{ setTimeout(() => { resolve("value"); }, 2000);})
}
async function implicitPromiseTest() {
const val = await getPromise();
console.log(val); // prints "value"
}
But this raises questions for me:
-
How can I resolve a Promise returned implicitly from an async function, without explicitly returning a Promise from that function’s block? Such a feature would allow me to simplify my code substantially.
-
If this can’t be done, then what even is the point of async always implicitly returning Promises? What’s the use of those Promises if they can’t be resolved or rejected?
-
In my 2nd function, the
await
keyword should halt function execution until the Promise fromgetPromise()
is resolved. And since it never resolves, the execution should stop there indefinitely. Then why does theconsole.log
execute?
2
Answers
It’s only going to wrap a promise around things you return from the async function. A value returned in a
setTimeout
callback function is going to be ignored.So what’s happening is: your
async
function starts running, then sets up a timeout, then implicitly returnsundefined
. Since this is anasync
function, thatundefined
gets wrapped in a promise. That promise is in a resolved state, since the function is done executing. Some time later the timeout goes off, but that has no effect on the async function which has already finished resolving its promise.async/await exists to simplify the code for working with already-existing promises. It does not take non-promise code and convert it to promise code, except for the case where you return a non-promise. If you want to create a promise from
setTimeout
or any other callback-based function, then you must use thenew Promise
constructor.So you don’t have to end all your async functions with
return Promise.resolve(someValue)
. You can instead doreturn someValue
eg:
That’s a false premise. In your first example the promise resolves right away, in your second example it resolves when the timeout goes off. Now that it’s resolved, any code that was awaiting it can continue
It’s important to remember that
async
/await
is just a different way of writing asynchronous code, and implemented as normal Promises under the hood. These keywords can turn Promise chains or nested promises into neater, easier to read sequential statements that look more like the rest of your synchronous code.Let’s break down the parts of your first example, since there are some things here that seem to be causing some confusion.
Default return value of
async function
As you note, async functions always return promises.
This is true even when the function has no
return
– in this case, a Promise which resolves to the valueundefined
will be returned; there won’t be anything for the caller to "wait for" in that case, since the returned Promise will already be settled, and can resolve toundefined
immediately on return.setTimeout
Your example seems to treat
setTimeout
as asynchronous, but it’s not async in the Promise sense– code that callssetTimeout
doesn’t wait for it to fire before continuing on.This means that
getPromise()
callssetTimeout
and then immediately returnsundefined
thereafter. Making the functionasync
just changes how thatundefined
is wrapped in the return to its caller, and doesn’t at all change howsetTimeout
is treated.This is more obvious if we log from
setTimeout
‘s callback; it fires well aftergetPromise
is already done.await
await
unwraps/ resolves expressions; if youawait
a Promise, you get the fulfillment value of that Promise (once it finishes), and if you await a non-Promise, you get the value directly (e.g.await 3
just gives you3
).In your code, by calling
await getPromise()
, you’re resolving the Promise that’s returned by theasync function
; and sincegetPromise
has no explicit return, it returnsundefined
, which is then what the Promise fulfills with, and whatawait
unwraps & returns.If you call
getPromise()
withoutawait
, you’ll get the bare promise, which you can then manually chain.then
on.You’re already doing this with
await getPromise()
! It just doesn’t feel like it because of the confusions explained above.They can and they are; you’re just
await
ing it immediately, which means you never see the Promise itself.Your code is effectively doing
await Promise.resolve(undefined)
, which simply yieldsundefined
.Because the Promise does resolve, fulfilled with the return value of
getPromise
, which isundefined
.