JavaScript 顶层 await

深入解析 JavaScript 顶层 await:ES2022 引入的特性、模块初始化场景、使用限制,以及与 CommonJS require 的对比。

为什么需要顶层 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'); // 异步

延展阅读