Glossary / Top-level await
Top-level await
Top-level await permits await directly in an ECMAScript module body, without wrapping code in an async function. The module’s evaluation becomes asynchronous and dependent modules wait for its completion. This is useful for module initialization that genuinely requires asynchronous setup, but it affects dependency loading and error propagation across the module graph.
Module evaluation effects
const config = await loadConfig();
export const client = createClient(config);
Top-level await is valid in modules, not classic scripts or CommonJS source in the same syntax form. A module that awaits blocks evaluation of importers that depend on it, though unrelated modules may proceed. Cycles involving top-level await can produce complex dependency ordering and deadlocks in module evaluation if modules wait on one another through cycles.
Use it sparingly for stable initialization, not as a way to hide application startup flow. Consider exporting an initialization Promise or an async factory when consumers need explicit lifecycle control. Handle failures at an appropriate startup boundary; a rejected top-level await rejects module evaluation and can prevent the importer from loading. Toolchains, test runners, and server module formats must support it.
Interview questions
- Where is top-level await allowed? In ECMAScript modules.
- What happens to importers? They wait for asynchronous evaluation of dependencies to complete.
- Why can cycles be problematic? Cyclic modules with async dependencies can wait on one another and complicate evaluation.
- When avoid it? When startup ordering should remain explicit or broad dependency graphs would be blocked by async initialization.