Stop writing (async () => {})().
We've all done it. To use await at the root of a file, we had to wrap everything in an Immediately Invoked Function Expression (IIFE).
Top-Level Await makes modules implicitly async. The module system pauses execution until the promise resolves.
02. Clean Initialization
This is perfect for modules that rely on asynchronous data to initialize their exports.
Old (Export Promise)
// db.js
let db;
export const init = async () => {
db = await connect();
};
export const getDb = () => db;
// consumer.js
await init(); // 𤢠Must remember to call
getDb().query(...);
New (Export Value)
// db.js
const db = await connect();
export default db; // š Ready to use!
// consumer.js
import db from './db.js';
// Module waits for connection!
db.query(...);
03. Double-Edged Sword
Be careful. If efficient parallelization isn't managed, a top-level await can block the importing of other modules.
db.js takes 10 seconds to connect, your entire app startup pauses for 10 seconds before executing the next line of import in the consumer.
Deep Dive: Parallel Loading
The Javascript module loader is smart. If two modules don't depend on each other, it tries to load them in parallel.
However, if `App` imports `User` which imports `DB`, you are stuck in a serial chain. Top-Level Await effectively pauses the graph construction at that node.
04. The Senior Engineer's Take
Use Sparingly
Top-Level Await is a fantastic tool for Service Initialization (DBs, Wasm loading, Config fetching).
Do NOT use it for heavy computation or slow network requests that aren't critical to app startup.