← 返回目录

18. Top-level Await

以前 await"只能在 async 房间里用的工具"——想等结果必须先包一层 async 函数再调用,像套了个套娃。ES2022 起,在模块(type="module")的最顶层也能直接 await——工具直接放客厅,不用再进房间拿。

18.1 互动演示(模拟模块顶层)

普通 script 里不能顶层 await(会报错),这里用 async 包装模拟模块里的顶层行为。

18.2 知识点讲解

① 以前 vs 现在

以前想等一个 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 + 调用 的壳,加载配置、初始化数据时更直接。

② 注意:必须是模块(type="module")

顶层 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 函数。

③ 顶层 await 会"阻塞"同模块后续代码

// main.js(模块)
let data = await fetchData(); // 等它完成(顶层 await 会卡住后面代码)
initPage(data); // 之后才执行 —— 依赖顺序有保证

要注意:顶层 await 会阻塞模块里后面的代码执行,但不会阻塞其他模块。适合"必须先拿到数据才能初始化"的场景——比如先加载用户配置再渲染页面。

18.3 实战:用在哪 / 常见坑 / 怎么解决

① 可能在什么地方用:模块加载时先拉配置/翻译表再初始化、Vite/Webpack 项目里做异步依赖初始化、动态 import 后直接 await 拿模块。

② 常见的问题:普通 <script> 里写顶层 await 直接报错;误以为顶层 await 会阻塞整个浏览器(其实只阻塞同模块);顶层 await 后面的代码被延迟导致界面白屏。

③ 解决思路:用顶层 await 前先确认 type="module";普通脚本继续用 (async => { ... }) 包装;初始化重活放后面,别让顶层 await 拖慢首屏。

一句话:顶层 await = 在模块最外层直接等结果,不用再包一层 async 函数;普通 script 里用不了。