async函数通过微任务队列调度与生成器暂停/恢复实现,不创建线程;必返回promise,await本质是切出并注册微任务;其协程结构由生成器提供骨架、promise承载状态,恢复时机由微任务队列决定。

async 函数的物理执行机制,不是“自动变异步”的魔法,而是由微任务队列调度 + 生成器暂停/恢复能力共同实现的确定性过程。它不创建新线程,也不改写 JavaScript 单线程本质,只是在事件循环框架内,用可预测的方式安排代码片段的执行时机。
async 函数返回 Promise 是强制语义,不是可选行为
只要函数声明为 async,无论有没有 await、有没有 return,JavaScript 引擎都会把它包装成一个立即返回 Promise 的函数:
- 函数正常结束且有 return 值 v → 等价于 Promise.resolve(v)
- 函数中途 throw 错误 e → 等价于 Promise.reject(e)
- 函数无 return 或 return undefined → 等价于 Promise.resolve(undefined)
这意味着调用 async 函数那一刻,主线程就拿到一个 Promise 实例,后续逻辑(比如 .then)可以立刻注册,但实际执行要等微任务调度。
await 不是“等待”,而是“切出 + 注册微任务”
await 表达式执行时,并不会卡住线程或让出 CPU,它的物理动作分两步:
- 对右侧表达式求值:若结果是 Promise,则提取其 then 方法;若不是(如数字、字符串),则自动包装为 Promise.resolve(值)
- 将 await 后面的所有语句(即当前 async 函数剩余部分)封装成一个回调函数,通过 .then() 注册进微任务队列
这个回调会在当前宏任务(如 script 主体、setTimeout 回调)结束后,被事件循环统一取出并执行——这就是为什么 console.log('2') 总在 'b' 之后、而不是紧接 '1' 输出。
生成器是 async/await 的结构骨架,Promise 是状态载体
Babel 等转译器把 async/await 编译为 Generator + Promise 组合,揭示了其协程本质:
- async 函数体被转为 function* 生成器函数,每遇到 await 就对应一次 yield
- 外部执行器(如 spawn)用 Promise.then() 监听 yield 出来的 Promise,一旦 settled,就调用 gen.next(value) 恢复执行
- 如果 Promise reject,则执行器调用 gen.throw(err),触发生成器内部的 try/catch
也就是说,async 函数的“暂停”靠 yield 实现,“恢复”靠 Promise 的 then 回调驱动,两者缺一不可。
微任务队列决定恢复时机,而非 Promise 创建顺序
多个 await 的执行顺序,取决于它们所依赖的 Promise 进入微任务队列的先后,而不是代码书写顺序:
- Promise.resolve().then(() => console.log('a')) 和 await Promise.resolve() 都会进入微任务队列,但后者注册的回调更晚(因需先走完生成器调度链)
- 两个 await 分别等待不同 Promise 时,谁先 settled,谁的后续微任务就先被执行
- 即使 Promise 是同步 resolve 的(如 Promise.resolve(1)),其 then 回调也一定在当前宏任务末尾执行,不会插队
这种机制保证了异步逻辑的可预测性:所有微任务总在宏任务之间清空,没有中间态干扰。











