async/await 为什么是语法糖async/await 是对 Promise 和 Generator 函数 的一种高级抽象和封装,目的是让异步代码的编写和阅读看起来更像是同步代码,极大地提高了代码的可读性和可维护性,因此被称为“语法糖”(Syntactic Sugar)。
它并没有引入新的底层异步机制,它的所有功能都可以通过 Promise 的链式调用 .then() 来实现。
async/await 的底层实现原理async/await 的实现依赖于 Generator 函数(或类似的迭代器机制)和 Promise。
一个被 async 修饰的函数(async function)在执行时,会被转换成一个 Generator 函数,并自动使用一个执行器(Executor/Runner)来管理和推进其内部的执行流程。
| 步骤 | 描述 | 对应的代码结构 |
|---|---|---|
I. 遇到 await | 当执行流遇到 await expression 时,它会暂停(yield),并将 expression 的值(通常是一个 Promise)传递给执行器。 | 相当于 Generator 函数中的 yield promise。 |
| II. 暂停执行 | async 函数立即返回一个 Promise 对象,并将当前函数体挂起。 | Generator 函数通过 yield 暂停。 |
| III. 等待结果 | 执行器接管,等待 await 后面的 Promise 完成(无论是 resolve 还是 reject)。 | 执行器监听 promise.then()。 |
| IV. 恢复执行 | Promise 状态确定后,执行器获取结果,并使用这个结果作为参数,调用 Generator 函数的 next() 方法,将执行流恢复到暂停的位置,并继续执行后续代码。 | 执行器调用 generator.next(result)。 |
| V. 返回最终值 | async 函数内部返回的值(return value)会被用来 resolve 之前返回的 Promise。如果没有 return,则返回 undefined。 | 执行器捕获 Generator 函数执行完毕的信号,并完成最初返回的 Promise。 |
yield 的对应关系我们可以粗略地将 async/await 结构视为以下 Generator 函数及其执行器的简化:
async/await 代码 | 转换为 Generator + 执行器 | 描述 |
|---|---|---|
async function fn() { ... } | function* fn() { ... } + run(fn) | async 函数本身就是一个被自动执行的 Generator 函数。 |
const result = await promise; | const result = yield promise; | await 相当于 yield,它将一个 Promise 抛出,暂停函数执行,等待 Promise 解决后,再将结果通过 next() 方法传回来。 |
await 表达式后面通常是一个 Promise,它负责将异步操作的结果或错误进行规范化封装。async 函数的返回值总是被封装成一个 Promise 对象。这确保了 await 关键字可以统一处理任何 async 函数的返回结果。async/await 的优势正是因为它是 Promise 的语法糖,才带来了这些优势:
try...catch 语句来处理异步操作的错误,而不需要在每个 .then() 后面添加 .catch()。if/else 或 for 循环中使用异步操作变得直观,逻辑流程更接近同步思维。