How can I use async/await at the top level?
I have been going over async
/await
and after going over several articles, I decided to test things myself. However, I can't seem to wrap my head around why this does not work:
async function main() {
var value = await Promise.resolve('Hey there');
console.log('inside: ' + value);
return value;
}
var text = main();
console.log('outside: ' + text);
The console outputs the following (node v8.6.0) :
> outside: [object Promise]
> inside: Hey there
Why does the log message inside the function execute afterwards? I thought the reason async
/await
was created was in order to perform synchronous execution using asynchronous tasks.
Is there a way could I use the value returned inside the function without using a .then()
after main()
?
I can't seem to wrap my head around why this does not work.
Because main
returns a promise; all async
functions do.
At the top level, you must either:
-
Use a top-level
async
function that never rejects (unless you want "unhandled rejection" errors), or -
Use
then
andcatch
, or -
Use top-level
await
(ES2022, broadly supported in modern environments) that allows top-level use ofawait
in a module.
#1 - Top-level async
function that never rejects
(async () => {
try {
var text = await main();
console.log(text);
} catch (e) {
// Deal with the fact the chain failed
}
})();
Notice the catch
; you must handle promise rejections / async exceptions, since nothing else is going to; you have no caller to pass them on to. If you prefer, you could do that on the result of calling it via the catch
function (rather than try
/catch
syntax):
(async () => {
var text = await main();
console.log(text);
})().catch(e => {
// Deal with the fact the chain failed
});
...which is a bit more concise (I like it for that reason).
Or, of course, don't handle errors and just allow the "unhandled rejection" error.
#2 - then
and catch
main()
.then(text => {
console.log(text);
})
.catch(err => {
// Deal with the fact the chain failed
});
The catch
handler will be called if errors occur in the chain or in your then
handler. (Be sure your catch
handler doesn't throw errors, as nothing is registered to handle them.)
Or both arguments to then
:
main().then(
text => {
console.log(text);
},
err => {
// Deal with the fact the chain failed
}
);
Again notice we're registering a rejection handler. But in this form, be sure that neither of your then
callbacks doesn't throw any errors, nothing is registered to handle them.
#3 top-level await
in a module
You can't use await
at the top level of a non-module script, but top-level await
allows you to use it at the top level of a module. It's similar to using a top-level async
function wrapper (#1 above) in that you don't want your top-level code to reject (throw an error) because that will result in an unhandled rejection error. So unless you want to have that unhandled rejection when things go wrong, as with #1, you'd want to wrap your code in an error handler:
// In a module, once the top-level `await` proposal lands
try {
var text = await main();
console.log(text);
} catch (e) {
// Deal with the fact the chain failed
}
Note that if you do this, any module that imports from your module will wait until the promise you're await
ing settles; when a module using top-level await
is evaluated, it basically returns a promise to the module loader (like an async
function does), which waits until that promise is settled before evaluating the bodies of any modules that depend on it.
Top-Level await
has moved to stage 3, so the answer to your question How can I use async/await at the top level? is to just add await
the call to main()
:
async function main() {
var value = await Promise.resolve('Hey there');
console.log('inside: ' + value);
return value;
}
var text = await main();
console.log('outside: ' + text)
Or just:
const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)
Compatibility
-
v8 since Oct 2019
- the REPL in Chrome DevTools, Node.js and Safari web inspector
-
Node v13.3+ behind the flag
--harmony-top-level-await
- TypeScript 3.8+ (issue)
- Deno since Oct 2019
- [email protected]