⏰ Async Without Async Function Wrapper
Need await at top level? Used to need IIFE.
Top-level await
works directly in ES modules. Cleaner initialization code.
❌ Old Way (IIFE)
(async () => {
const data = await fetch('/api/config');
const config = await data.json();
startApp(config);
})();
✅ Top-Level Await
// In .mjs or type="module" script
const response = await fetch('/api/config');
const config = await response.json();
startApp(config);
🎯 Real-World Examples
// Database connection (Node.js)
import { createConnection } from 'mysql2/promise';
const db = await createConnection({
host: 'localhost',
user: 'root',
database: 'myapp'
});
export { db };
// Dynamic imports
const math = await import('./math.js');
console.log(math.add(2, 3));
// Reading files (Node.js)
import { readFile } from 'fs/promises';
const packageJson = JSON.parse(
await readFile('./package.json', 'utf-8')
);
export const version = packageJson.version;
// API initialization
const users = await fetch('/api/users')
.then(r => r.json());
const products = await fetch('/api/products')
.then(r => r.json());
export { users, products };
✅ Important Notes
-
Only works in ES modules
(type="module") - Top-level await blocks module evaluation
- Importing modules with top-level await → import waits
- Useful for module initialization that needs async
💡 Use Cases
- Loading configuration before app starts
- Database connection in Node.js modules
- Dynamic imports based on conditions
- Reading environment-dependent resources
- Initializing SDKs that need async setup
“Every API module started with async IIFE.
Top-level await removed wrapper.
Code cleaner, startup logic obvious.
ES modules are finally complete.”
