async/await feature introduced in ECMAScript 2017
enables asynchronous, promise-based behavior to be written in a cleaner style
avoiding the need for promise chains.
await keyword can only be used inside an async function.
Attempting to use an await outside of an async function results in a SyntaxError -
SyntaxError: await is only valid in async function.
The ECMAScript feature ‘Top-level await’ which is
promoted to Stage 4 in the TC39 process
lets us use the asynchronous
await operator at the top level of modules.
Top-level await enables modules to act as big async functions.
With top-level await, ECMAScript Modules (ESM) can await resources.
Other modules which import them have to wait before evaluating their code.
Sounds great, right?
Let us dive more into it.
As await is only available within async functions, a module can include an await in the code by wrapping the code in an async function.
Immediately invoked top-level async function(IIAFE)
We can also use immediately invoked async function expression(IIAFE).
But this has a downside.
undefined directly after importing.
We must wait until the asynchronous work is finished before we can access it.
This approach is not safe. It will not work if the async function takes longer than 100 milliseconds.
Export a Promise to represent initialization
Another approach is to export a promise letting the import module know that the data is ready.
Though this approach seems to give the desired result, it has some limitations.
- Importing module must be aware of the pattern and should use it correctly.
- If we forget to apply the pattern, we might get the desired result sometimes.
Top-level await eliminates all downsides of all the approaches we discussed.
Module execution order
- Modules follow the same post-order traversal as established in ES2015.
- If a module reaches an await, it will yield control and let other modules initialize themselves in the same well-specified order.
- Execution of the module starts with the deepest imports.
- After a top-level await is reached, the control is passed to start the next module or to other asynchronously scheduled code.
Top-level await is very helpful for the below use cases -
Support for Top-level await -
Check out more details on Top-level await proposal and v8.dev.