Top-level await enables developers to use the await keyword outside of async functions. It acts like a big async function causing other modules who import them to wait before they start evaluating their body.
When async/await was first introduced, attempting to use an await outside of an async function resulted in a SyntaxError.
await Promise.resolve(console.log('🎉'))
// → SyntaxError: await is only valid in async function
Many developers utilized immediately-invoked async function expressions as a way to get access to the feature.
(async function() {
await Promise.resolve(console.log('🎉'))
// → 🎉
})()
Or also declare an async function and then call it:
const doSomething = async () => {
await Promise.resolve(console.log('🎉'))
// → 🎉
}
doSomething()
With top-level await, the above code instead works the way you’d expect within modules:
await Promise.resolve(console.log('🎉'))
// → 🎉
Note: Top-level await only works at the top level of modules. There is no support for classic scripts or non-async functions.
const strings = await import(`/i18n/${navigator.language}`)
This allows for modules to use runtime values in order to determine dependencies. This is useful for things like development/production splits, internationalization, environment splits, etc.
const connection = await dbConnector()
This allows modules to represent resources and also to produce errors in cases where the module cannot be used.
The following example attempts to load a JavaScript library from CDN A, falling back to CDN B if that fails:
let jQuery
try {
jQuery = await import('https://cdn-a.example.com/jQuery')
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery')
}