异步函数返回的Promise在console.log时已敲定的原因及两种写法的差异解析
异步函数返回的Promise在console.log时已敲定的原因及两种写法的差异解析
嘿,这个问题问得特别精准,刚好戳中了async函数的核心机制、Promise调度和JavaScript事件循环的关键细节,咱们一步步拆解明白:
一、第一个示例:为什么Promise会立即处于敲定状态?
先明确一个容易被忽略的核心规则:没有await的async函数,本质就是一个同步执行的函数,只是会自动把返回值/抛出的错误包装成Promise而已。
咱们把第一个示例的执行流程拆成毫秒级的步骤:
- 调用
get_exam_score(),函数入栈同步执行:- 同步计算
exam_score = Math.random() * 10; - 同步判断分数:如果小于5,直接抛出错误;如果大于等于5,函数体执行完毕(无return语句,默认返回
undefined)
- 同步计算
async函数的自动包装逻辑同步触发:- 如果函数抛出错误,立刻包装为已拒绝(rejected)的Promise
- 如果函数正常执行完毕,立刻包装为已兑现(resolved)的Promise(值为
undefined)
- 这个已经敲定的Promise被同步返回给变量
promise - 执行
console.log("TRY:", promise);——此时promise自然是敲定状态
你之前的误解点在这里:只有当async函数里出现await时,才会触发微任务调度,让后续逻辑进入微任务队列。而这个示例里没有任何await,函数体全程在调用栈里同步执行完,根本没有生成微任务,所以Promise的敲定是同步完成的,完全不需要等调用栈清空。
二、第二个示例:为什么Promise会处于pending状态?
核心差异在于:这个示例里的async函数,同步返回了一个你手动创建的、本身就处于pending状态的Promise。
咱们梳理这个流程:
- 调用
get_exam_score(),函数入栈同步执行:- 执行
return new Promise((resolve, reject) => {...}) new Promise的执行器(executor)函数是同步执行的,但如果你的执行器里没有立即调用resolve/reject(比如把resolve/reject的调用放到了setTimeout这类异步操作里),这个Promise就会保持pending状态
- 执行
async函数的规则是:如果返回值本身是一个Promise对象,就直接返回这个Promise,不会再做额外包装- 所以变量
promise拿到的就是这个pending的Promise,此时执行console.log,自然会看到pending状态
举个更直观的反例:如果手动创建的Promise执行器里立即调用resolve,结果也会是敲定状态:
async function get_exam_score() { return new Promise((resolve) => { resolve("You passed"); // 同步调用resolve }); } const promise = get_exam_score(); console.log(promise); // 显示已兑现状态
三、系统梳理JavaScript的调用栈、微任务、事件循环和async函数的交互
为了彻底搞透,咱们把关键机制串起来:
- 调用栈:所有同步代码都会依次进入调用栈执行,执行完成后出栈,直到调用栈完全清空
- 微任务队列:存放Promise的
then/catch/finally回调、process.nextTick等任务。调用栈清空后,事件循环会优先清空所有微任务,直到微任务队列为空,才会处理宏任务(比如setTimeout回调) async函数的核心行为逻辑:- 无
await时:同步执行完整函数体,同步包装返回值/错误为敲定的Promise返回,无任何异步调度 - 有
await时:- 遇到
await后,暂停函数执行,把函数后续的逻辑打包成一个微任务 - 立即返回一个
pending的Promise - 当
await后面的Promise敲定后,这个微任务会被加入微任务队列 - 调用栈清空后,微任务执行,函数后续逻辑继续运行,最终把返回的Promise状态改为敲定
- 遇到
- 无
比如给第一个示例加个await,结果就会完全不同:
async function get_exam_score() { await Promise.resolve(); // 触发微任务调度 const exam_score = Math.random() * 10; if (exam_score < 5) { throw("You failed"); } } const promise = get_exam_score(); console.log("TRY:", promise); // 此时promise是pending状态!
因为await暂停了函数,返回了pending Promise,后续逻辑在微任务队列里,调用栈还没清空,所以console.log时Promise还没敲定。
核心差异总结
第一个示例(无await,自动包装Promise) | 第二个示例(手动返回pending Promise) |
|---|---|
async函数同步执行完所有逻辑,自动生成已敲定的Promise返回 | async函数同步返回一个手动创建的pending Promise(其resolve/reject会在后续异步时机执行) |
| 全程无异步操作,无任何微任务生成 | 手动Promise的状态变更逻辑被调度到后续的宏/微任务,当前时刻Promise仍为pending |
备注:内容来源于stack exchange,提问作者Oriol Serrabassa
相关产品推荐
相关产品推荐

