async 函数会自动返回一个 Promise 对象,即使你只返回一个普通值,其效果也等同于 Promise.resolve()。而 await 关键字只能在 async 函数内部使用,它会暂停当前函数的执行——注意,仅暂停这一小段逻辑,并不会阻塞主线程——等待该 Promise 完成之后,再继续向下执行。

首先介绍 async 关键字。它本身并不改变函数内部的同步执行顺序,但会完成两件关键任务:一是让函数自动返回一个 Promise,二是允许在函数体内使用 await 来暂停等待异步操作。这两点看似简单,但实际编码时很容易踩坑。
函数返回值变成 Promise
给函数添加 async 关键字后,即使你只返回一个普通值,例如 return 42,实际返回的也是一个已 resolve 的 Promise。具体表现如下:
async function f() { return 42; }→ 调用f()返回的是Promise.resolve(42),而不是数字 42- 如果函数内部抛出错误,返回的 Promise 会自动变为
Promise.reject(error),无需手动 catch 包装 - 即使函数体内没有 await,它也会强制返回 Promise——这是语言层面的语义,无法绕过
启用 await 语法
只有进入 async 函数,你才能合法使用 await。它的作用简单明了:暂停当前 async 函数的执行(注意,并不阻塞主线程),等待右侧的 Promise 状态 settled 之后,再继续执行后续代码。
await fetch('/api')会等待网络请求完成,再往下执行;如果请求失败,它会抛出异常,可用 try/catch 捕获- await 只对 Promise 生效;如果右边是一个普通值,它会立即返回,效果等价于
Promise.resolve(value) - 不要把 await 理解为“让代码变慢”——其真正价值在于让异步逻辑可以按顺序书写,避免嵌套回调的噩梦
执行时机没变,但调用方式变了
async 函数仍然是同步开始执行的。函数体第一行代码(例如 console.log)会立即执行,不会等到你调用它才运行。只有在遇到 await 时,才会“让出”控制权。
- 函数体开头的同步代码(例如
console.log("start"))仍然立即执行 - await 后面的代码会被放入微任务队列,等待当前同步任务全部结束之后才运行
- 调用 async 函数时,必须使用
.then()或者在外部再用await来处理返回的 Promise,不能直接获取返回值
不能在顶层 await(除非模块顶层)
由于 await 只能在 async 函数内使用,所以如果在模块最外层直接写 await fetch('/data'),会报错。有两种常见解决方案:
- ❌
await fetch('/data');(直接写在 script 最外层会导致报错) - ✅ 包一层 async IIFE:
(async () => { const res = await fetch('/data'); })(); - ✅ 在 .mjs 文件或支持 top-level await 的环境(ES2022+,仅限模块环境)中,可以直接在顶层使用
await fetch('/data');
这些规则看起来并不复杂,但实际编码时很容易忽略——尤其是忘记 async 函数返回值已经变成了 Promise,以及调用时忘记使用 await 或 then 来获取结果。从实践经验来看,大部分新手踩坑都出在这两个地方。
