遇到这么一个面试题
async function async1() {
return Promise.resolve(1);
}
async1().then((res) => {
console.log(res);
})
new Promise((resolve) => {
resolve('2');
}).then((res) => {
console.log(res);
}).then(() => {
console.log(3);
})
要求说出代码执行结果,很多人说1,2,3;但其实是2,3,1
核心机制:async
函数返回 Promise 的包装行为
当 async
函数返回一个 Promise 时,引擎会执行以下步骤:
- 隐式包装
async
函数始终返回一个新的 Promise(记作p
)。
如果函数内部返回一个非 Promise 值(如return 1
),p
会直接以该值解决,无需额外步骤。 - 返回 Promise 时的特殊处理
如果函数内部返回一个 Promise(如return Promise.resolve(1)
),引擎会:
-
- 解包(Unwrap):等待这个内部 Promise 解决。
- 同步状态:将
p
(外层 Promise)的状态与内部 Promise 同步。 - 触发微任务:此过程需要 至少一个额外的微任务 来完成同步。
具体代码分析
以下面的代码为例:
async function async1() {
return Promise.resolve(1); // 返回一个 Promise
}
执行步骤详解:
- 调用
async1()
-
- 引擎创建一个新的 Promise
p1
(由async
隐式生成)。 - 执行函数体,遇到
return Promise.resolve(1)
,记内部 Promise 为p2
。
- 引擎创建一个新的 Promise
- 解析
p2
到p1
-
- 引擎需要将
p1
的状态与p2
同步。 - 通过调用
p2.then(
实现同步。
(value) => resolve(p1, value),
(error) => reject(p1, error)
) - 这一步会生成一个微任务(等待
p2
完成)。
- 引擎需要将
- 微任务队列的变化
-
- 当
p2
是已解决的 Promise(如Promise.resolve(1)
),其.then()
回调(同步p1
状态)会被放入微任务队列。 - 此时,
async1().then(...)
的回调(即console.log(res)
)需等待p1
解决,而p1
的解决又依赖上述微任务。
- 当
得出代码执行顺序
- 初始微任务队列:
-
[处理 new Promise 的 .then(res => console.log(2))]
- 第一轮微任务处理:
[
() => console.log(3),
() => console.log(1)
]
-
- 执行
console.log(2)
→ 输出 2。 - 链式调用
.then(() => console.log(3))
的回调进入队列。 - 同时,
async1()
的第一个微任务(解析内部 Promise)完成,其外层 Promise 的.then()
回调(console.log(1)
)进入队列。
此时队列变为:
- 执行
- 第二轮微任务处理:
-
- 执行
console.log(3)
→ 输出 3。 - 执行
console.log(1)
→ 输出 1。
- 执行