第一个问题解答
为什么这段代码先输出 "3" 后输出 "2"?
async function p2() {
return Promise.resolve('p2');
}
async function p3() {
return await Promise.resolve('p3');
}
p2().then((res) => console.log(res));
p3().then((res) => console.log(res));
解释:
p2
函数:这个函数通过 return Promise.resolve('p2');
直接返回了一个已解决的 Promise。注意这里并没有使用 await
,所以函数执行到 return
时就会立即返回一个 Promise 对象,而不会等待 Promise 解决。p3
函数:这个函数通过 return await Promise.resolve('p3');
返回了一个 Promise。由于使用了 await
,函数会等待 Promise.resolve('p3')
解决后再继续执行并返回结果。尽管 Promise.resolve('p3')
几乎立即解决,但使用 await
会使得 p3()
函数的执行暂停,直到 Promise 解决。- 执行顺序:由于
p2()
立即返回一个 Promise 并继续执行 .then()
链,而 p3()
等待其内部的 Promise 解决后再继续,因此 p2()
的 .then()
链会先被添加到微任务队列中。当当前执行栈清空后(即同步代码执行完毕后),JavaScript 引擎会处理微任务队列中的任务,因此 p2().then()
中的 console.log(res)
会先执行,输出 "2"。随后,p3()
中的 Promise 解决,p3().then()
中的 console.log(res)
才会执行,输出 "3"。
第二个问题解答
为什么这段代码交错输出 "142536"?
new Promise((r) => {
r();
})
.then(() => console.log(1))
.then(() => console.log(2))
.then(() => console.log(3))
new Promise((r) => {
r();
})
.then(() => console.log(4))
.then(() => console.log(5))
.then(() => console.log(6))
解释:
- Promise 链的执行:两个 Promise 链几乎是同时被创建的,并且它们各自内部的 Promise 都是立即解决的(通过
r();
)。但是,每个 .then()
方法调用都会将一个新的回调函数添加到各自的 Promise 解决后的微任务队列中。 - 微任务队列:JavaScript 有一个微任务队列,用于存放需要在当前执行栈清空后立即执行的异步操作(如 Promise 的解决回调)。由于这两个 Promise 链都是立即解决的,它们的
.then()
回调会被依次添加到微任务队列中。 - 执行顺序:当同步代码执行完毕后,JavaScript 引擎会检查微任务队列,并按顺序执行队列中的任务。因此,第一个 Promise 链的
.then()
回调(输出 "1"、"2"、"3")会按照它们被添加到队列中的顺序依次执行,随后是第二个 Promise 链的 .then()
回调(输出 "4"、"5"、"6")。这就导致了交错输出 "142536"。
总结:尽管这两个 Promise 链在代码中看起来是连续书写的,但它们各自的 .then()
回调会根据它们被添加到微任务队列的顺序来执行,而不是根据它们在代码中的位置。
第一个问题比较取巧。差异的部分就是如果
async
函数中await
的处理过程。这个部分稍微转换一下能够帮助你的理解:
输出结果是这样的:
可以看到,在执行到
证明p3/await是同步任务
时,p3
这个async
函数的执行被暂停了(p3.then()
此时被入栈),继续回到主进程执行剩余的业务代码。当主进程执行完毕之后,立即回到了
p3.await
之后的代码块恢复p3
函数的执行(因为p3.await
已经被兑现了resolve
)。当
p3
函数全部执行完毕之后,此时和我们认知冲突的部分就开始出现了。执行完return p3v
之后 并没有按照p2.then
、p3.then
的入栈顺序开始执行p2.then()
,而是直接执行了p3.then()
中的内容。为什么会这样,AI回答了一部分也和我的猜想是相符合的,就是返回的值是有一些区别的:
也就是说,
p2
函数返回了一个完整的Promise
对象(虽然它的状态是fulfilled
的),而p3
函数返回了一个Promise
被兑现之后的值,所以先输出了p3.then 执行
。随后p2()
中的Promise
被解决输出P2.then 执行
并不是完全因为
awiat
的执行暂停,如果稍作修改执行的结果就会完全不一样,即使修改后的代码也会等待p3
函数的await
执行结束,并返回一个和p2
函数一样的返回值。但其实实际业务中绝大部分时间我们并不会这样去用,基本上会用以下的方式来使用:
这样是不是执行结果就很明确了。
第二个问题就是简单的JS事件循环机制(
EventLoop
)了,按照执行顺序依次入栈出栈。表现出来的就是交替输出。没有什么好说的,稍微理解一下事件循环机制就好了。