以前 await 像"只能在 async 房间里用的工具"——想等结果必须先包一层 async 函数再调用,像套了个套娃。ES2022 起,在模块(type="module")的最顶层也能直接 await——工具直接放客厅,不用再进房间拿。
以前想等一个 Promise,得专门包一个 async 函数再马上调用,多写两行。现在模块最外层直接 await。
function getData() { // 一个返回 Promise 的函数
return new Promise(resolve => { // 造一个承诺
setTimeout(() => resolve("数据来了"), 500); // 500 毫秒后兑现
});
}
// 以前:非得套个 async 函数再调用
async function load() { // 必须包在 async 函数里
let data = await getData(); // await 只能在 async 里
console.log(data); // 等到了再打印
}
load(); // 还得手动调一下
// 现在:模块最外层直接等(ES2022)
let data = await getData(); // 不用包函数,直接等
console.log(data); // 等到了再打印
少一层 async function + 调用 的壳,加载配置、初始化数据时更直接。
顶层 await 不是随便哪个 script 都能用的——只有把当前文件当成 ES 模块才行。
// 要这么引用才是"模块",才能用顶层 await < script type = "module" src = "main.js" > < /script> // 或 package.json 里 "type": "module" // 普通 <script> 里写顶层 await → ★ 报错
如果你在普通 <script> 里直接写顶层 await,浏览器会报"await 只在模块顶层才允许"。用 Vite / Webpack / 原生 type="module" 的项目里很常见;普通 script 里想等结果,还是得包 async 函数。
// main.js(模块) let data = await fetchData(); // 等它完成(顶层 await 会卡住后面代码) initPage(data); // 之后才执行 —— 依赖顺序有保证
要注意:顶层 await 会阻塞模块里后面的代码执行,但不会阻塞其他模块。适合"必须先拿到数据才能初始化"的场景——比如先加载用户配置再渲染页面。
① 可能在什么地方用:模块加载时先拉配置/翻译表再初始化、Vite/Webpack 项目里做异步依赖初始化、动态 import 后直接 await 拿模块。
② 常见的问题:普通 <script> 里写顶层 await 直接报错;误以为顶层 await 会阻塞整个浏览器(其实只阻塞同模块);顶层 await 后面的代码被延迟导致界面白屏。
③ 解决思路:用顶层 await 前先确认 type="module";普通脚本继续用 (async => { ... }) 包装;初始化重活放后面,别让顶层 await 拖慢首屏。