← 返回目录

18. Top-level Await

顶层 await 就是在模块顶层直接写 await,不用包 async 函数。

核心规则:
① 只在 ES Module(type="module")里能用
② 顶层 await 会等异步代码跑完再执行

坑:普通 script 不能用、会阻塞模块加载、import 也能配合 await

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

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

18.2 知识点讲解

① 以前 vs 现在

以前想等一个 Promise,得专门包一个 async 函数再马上调用,多写两行。现在模块最外层直接 await。

// 一个返回 Promise 的函数
function getData() {

// 造一个承诺
  return new Promise(resolve => {

// 500 毫秒后兑现
    setTimeout(() => resolve("数据来了"), 500);
  });

// 以前:非得套个 async 函数再调用
}

// 必须包在 async 函数里
async function load() {

// await 只能在 async 里
  let data = await getData();

// 等到了再打印
  console.log(data);
}

// 还得手动调一下
load();

// 现在:模块最外层直接等(ES2022)
// 不用包函数,直接等
let data = await getData();

// 等到了再打印
console.log(data);

少一层 async function + 调用 的壳,加载配置、初始化数据时更直接。

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

顶层 await 不是随便哪个 script 都能用的——只有把当前文件当成 ES 模块才行。

// 要这么引用才是"模块",才能用顶层 await
<
script type = "module"

// 普通 <script> 里写顶层 await
src = "main.js" > < /script>     / / 或 package.json 里 "type": "module"

如果你在普通 <script> 里直接写顶层 await,浏览器会报"await 只在模块顶层才允许"。用 Vite / Webpack / 原生 type="module" 的项目里很常见;普通 script 里想等结果,还是得包 async 函数。

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

// main.js(模块)
// 等它完成(顶层 await 会卡住后面代码)
let data = await fetchData();

// 之后才执行 —— 依赖顺序有保证
initPage(data);

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

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

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

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

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

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

本页重点

API作用参数返回值代码示例
顶层 await在 ES 模块最外层直接等结果一个 Promise它的结果await getData();
type="module"只有 ES 模块最外层才允许直接 await写在 script 标签上一个 ES 模块<script type="module">