const a = () => {
return new Promise((resolve) => {
resolve(1);
});
};
const b = () => {
return new Promise((resolve) => {
a()
.then((aR) => {
console.log(aR);
resolve(2);
})
.finally(() => {
console.log("A");
});
});
};
const c = () => {
b().then((bR) => {
console.log(bR);
});
};
c();
今天有一段代码抽象后逻辑如上述代码,我本来觉得输出顺序应该是
1
A
2
但实际输出却是
1
2
A
不太理解是为什么,求指教。
环境:Chrome 版本 126.0.6478.115(正式版本) (64 位)
2024.6.24 14:50 更新:
首先感谢各位的答疑解惑,受益匪浅。
在上面的代码中,我觉得输出 1 A 2
是认为,当c
中运行 b()
时调用了函数b
,b
中运行了a
,并进入then
逻辑输出1
,then
完毕后执行finally
输出A
,此时b()
运行完毕,输出b的返回结果2
,所以顺序是 1 A 2
。
这里思考的逻辑我参考的是Java中的try catch finally。但现在我发现这本质上有所区别,应该结合浏览器的微队列,以及Promise的then与finally都会返回一个Promise对象并将其放到微队列中等待执行,其执行顺序则与放入微队列的先后顺序有关。
在b
中,当a()
运行得到返回结果1
后,进入then
逻辑输出了1,并返回了2,此时其实已经将结果封装为Promise对象并放入微队列中,后续finally
的内容虽然也会封装为Promise对象并放入微队列,但并不会对前者造成影响。
所以此时在c
中调用b()
并得到结果时,就会立即运行后续代码,而不会等待finally
的执行,因此实际运行时才会输出 1 2 A
。
当然,我现在也是对着答案想原因,下次遇到也不知道能不能在看到答案前想明白。
最后,再次感谢各位。
(以下的 queue 都指 microtask queue)
注意到
这是链式调用,
a().then(...)
返回一个Promise
,等这个Promise
fulfill 才会输出A
,而不是a()
fulfill 之后。这样的话resolve(2)
会先一步把b().then
放入 queue,然后才 resolvea().then
,然后再把这个a().finally
放入 queue写成这样会明白很多:
执行过程是这样的:
f
a
b
立即被 fulfill,导致cb1
进入 queue,现在 queue =[cb1]
c
进入 queuef
返回cb1
,现在 queue =[]
1
,resolvea
,导致cb2
进入 queue,现在 queue =[cb2]
cb1
执行结束,导致c
被 resolve,导致cbA
进入 queue,现在 queue =[cb2, cbA]
cb2
,现在 queue =[cbA]
2
cbA
,现在 queue =[]
A
,当前 tick 结束