Top-Level Await in ES Modules0%

Top-Level Await in ES Modules

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

Top-Level Await in ES Modules

Prior to ECMAScript 2022, the await keyword could only be executed inside functions marked with the async keyword. This forced developers to write awkward Immediately Invoked Async Function Expressions (IIAFE) to initialize databases or fetch remote configurations. With Top-Level Await, ES Modules can await promises directly at the root level of the module.


1. The Historical Workaround vs. Modern Top-Level Await

The Legacy Problem (IIAFE Boilerplate):

JavaScript
// LEGACY: Required async wrapping function!
let dbConnection;
 
(async () => {
dbConnection = await connectToDatabase();
})();
 
export { dbConnection }; // DANGER: Consumer may import dbConnection before promise resolves!

Modern ES2022 Top-Level Await:

JavaScript
// MODERN: Clean, synchronous-looking asynchronous execution!
const dbConnection = await connectToDatabase();
 
export { dbConnection }; // Guarantees dbConnection is fully resolved before consumers run!

2. Module Graph Execution Order

When a module uses top-level await, it pauses the execution of dependent modules that import it until the promise fulfills:

Output
Module A (await fetchConfig()) ──► Resolves after 200ms
▲
│ imports A
Module B (Waits for Module A to settle!)
│
▼
Module B executes with guaranteed initialized configuration!

Sibling Modules Run Concurrently!

If Module C imports both Module A and Module B, and both have top-level awaits, both modules fetch their resources concurrently:

JavaScript
// main.js
import './moduleA.js'; // Starts fetch A
import './moduleB.js'; // Starts fetch B (runs concurrently with A!)

3. Top Production Use Cases

1. Dynamic Resource Loading & Polyfills

JavaScript
// internationalization.js
const userLanguage = navigator.language || 'en';
const translations = await import(`./locales/${userLanguage}.json`, {
assert: { type: 'json' }
});
 
export default translations.default;

2. Live Database / Microservice Handshake

JavaScript
// dbClient.js
import { MongoClient } from 'mongodb';
 
const client = new MongoClient(process.env.DB_URI);
await client.connect(); // Halts module export until DB is live!
export const db = client.db('enterprise_prod');

3. Fallback Resource Selection

JavaScript
// apiConfig.js
let connection;
try {
connection = await fetch('https://primary.api.com/status');
} catch {
connection = await fetch('https://backup.api.com/status');
}
 
export const serverStatus = await connection.json();

4. Critical Warnings: Deadlocks and Waterfall Traps

  1. 1
    Avoid Circular Top-Level Awaits: If Module X awaits an import from Module Y, and Module Y awaits an import from Module X, a circular dependency deadlock occurs.
  2. 2
    Beware of Long Network Delays: A slow top-level await blocks the evaluation of every downstream module that imports it, delaying application startup.

Interactive Quiz

Practice Quiz

Question 1
In what environment is Top-Level Await valid?
Question 2
What happens to a module that imports another module containing a top-level await?
Question 3
How do sibling modules with top-level await behave when imported together?
Question 4
What common legacy workaround did top-level await replace?
Question 5
What is a key performance risk of using top-level await for slow network calls?
Next Lesson

Microtasks vs Macrotasks (Event Loop Deep Dive)

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext