为什么需要顶层 await
在 ES2022 之前,await 只能在 async 函数内部使用。但在 ES Module 中,经常需要在模块顶层等待异步操作完成,比如加载配置、初始化连接等。
一、基本用法
1.1 模块顶层 await
// module.js
const config = await fetch('/api/config').then(r => r.json());
export { config };
1.2 在模块初始化中使用
// database.js
import { db } from './connection.js';
export const users = await db.query('SELECT * FROM users');
// 模块初始化时等待数据库查询完成
二、使用场景
2.1 动态模块初始化
// feature.js
const enabled = await checkFeatureFlag('new-feature');
export { enabled ? await import('./new-feature.js') : null };
2.2 资源初始化
// cache.js
const cache = new Map();
export async function initCache() {
const data = await fetch('/api/cache-data').then(r => r.json());
data.forEach(item => cache.set(item.id, item));
}
export const ready = initCache(); // 初始化 Promise
2.3 错误处理
// 如果初始化失败,整个模块变为错误状态
try {
const config = await fetch('/api/config').then(r => r.json());
export { config };
} catch (e) {
export { error: e };
}
三、限制
3.1 条件 await
// 可以在条件语句中使用
const data = condition ? await fetch('/a') : await fetch('/b');
3.2 不能在常规函数中使用
function normalFunction() {
await doSomething(); // SyntaxError
}
async function asyncFunction() {
await doSomething(); // OK
}
四、与 CommonJS 对比
4.1 CommonJS 的同步加载
// CommonJS
const config = require('./config'); // 同步
4.2 ESM 的异步加载
// ESM
const config = await import('./config'); // 异步