ReviseAlgo Logo

Asynchronous JavaScript

Top-Level Await

Master JavaScript top-level await. Learn how ES Modules allow using await outside async functions, dynamic loading patterns, and module execution rules.

Last Updated: July 15, 2026 10 min read

1. Introduction

Historically, the await keyword could only be used inside functions declared with the async modifier. Top-Level Await is an ES2022 feature that allows you to use await at the top level of ES Modules, treating the module itself as a large asynchronous function.

2. Why It Matters

Top-level await simplifies initializations. It allows modules to fetch remote configurations, establish database connections, or load translations before executing their exports, ensuring that importing modules receive fully initialized resources.

3. Real-World Analogy

Think of a Theater Show Entrance Policy:

  • Traditional Await (Late Entry): The audience enters the theater. When the show starts, actors pause the play dynamically to fetch props or check cues. The show halts mid-scene.
  • Top-Level Await (Gate Checks): The theater doors remain locked until all actors have checked in, the stage is set, and the props are loaded (asynchronous initializations complete). Only when everything is ready do the doors open, ensuring the show runs smoothly from start to finish.

4. Module Execution Flow

Top-level await changes how modules are evaluated:
Block Import Execution: When a module uses top-level await, any other modules that import it wait to execute until the awaited promise resolves.
Parallel Parent Evaluation: Parent modules evaluate child imports in parallel where possible, but execution of the parent module's code is blocked until all child modules have finished initializing.

5. Common Use Cases

Top-level await is commonly used for:

  • Dynamic Dependency Loading: Loading specific modules dynamically based on runtime conditions (e.g. translation files).
  • Resource Initializations: Connecting to databases, loading environment configs, or registering plugins.
  • Fallback Modules: Loading a local fallback library if a remote CDN load request fails.

6. Common Mistakes

  • Blocking execution with slow requests: Since top-level await blocks execution of the importing module, running slow or unreliable requests can delay your application's start time. Use timeouts to prevent infinite blocks.
  • Trying to use it in CommonJS modules: Top-level await is exclusive to ES Modules. Using it in CommonJS modules (with require()) throws a SyntaxError.

7. Quick Quiz

Q1: Does a module that uses top-level await block the execution of other sibling modules imported by the same parent?

A) Yes, all siblings wait sequentially

B) No, sibling modules are evaluated in parallel; only the parent module is blocked

Answer: B — Sibling modules evaluate in parallel. The parent module is blocked until all of its imported modules resolve.

8. Scenario-Based Challenge

The Failover CDN Loader:

You load an external library: lodash. First, attempt to fetch it from a remote CDN using dynamic imports. If the request fails or times out, fall back to importing the local file. Write this logic using top-level await inside a wrapper module.

9. Debugging Exercise

Explain why this script crashes, and how to fix it:

// module.js (CommonJS setup)
const data = await fetch('/api/config').then(r => r.json()); // crashes!
module.exports = { data };
View Solution

Diagnosis: The file uses CommonJS module syntax (module.exports). Top-level await is only supported in ES Modules.

Fix: Convert the file to an ES Module by using export syntax and setting the project type to module in package.json:

// module.js (ES Module)
const data = await fetch('/api/config').then(r => r.json());
export { data };

10. Interview Questions

🟢 Q1: Explain how top-level await affects module loading and execution order in ES Modules.

Answer: When a module contains a top-level await, its execution is suspended until the awaited Promise resolves.
• Any modules importing the suspended module will also wait, blocking their execution until the dependency resolves.
• Sibling modules in the dependency graph continue to evaluate in parallel.
• This ensures that once the parent module runs, all of its imported dependencies are fully initialized.

11. Production Considerations

  • Avoid Infinite Blocks: When using top-level await to query external services during start time, always implement a timeout fallback to prevent the application from hanging indefinitely if the service goes down.