Promise魔鬼面试题-解析

时间:2024-05-08 12:52:15

难点分析

  1. 这题难点就在于 return Promise.resolve(4) 当返回一个 Promise 的时候是如何处理的,在 Promise a+ 规范中,写道,如图:

    image-20240508012830269

  2. 也就是说这个当前Promise(表示return Promise了的当前这个Promise) 要与返回的 Promise 保持一致,但是如何保持一致,标准里面并没有说到,也就说实际如何实现这个状态一致,并不关心,在 v8 中,实现的源码如图:

    image-20240508013128678

  3. 表示的意思是如果返回的是 Promise,它会去调用这个 Promise 的 then 方法,在这个 then 方法里面完成当前 Promsie,但是调用 then 方法完成当前这个 Promise 是会放入到一个微任务队列里面去完成的

  4. 至此,这个难点就已经分析完成了

分析输出

step1

我们回到题目本身,来分析一下输出,我们知道,一个 then 方法一定会返回一个 Promise,then 方法是同步的,then 方法里面的回调才是异步的,根据这个准则,我们可以先来确定一下初步的执行状态。

根据这个准则,执行的初步顺序如下:

  1. 第一个 Promise.reslove():记作 pr1,状态为 fulfilled
  2. then(()=>{console.log(0)}):记作 p0,状态为 pending,因为这些then里面的回调函数还是异步任务,目前处于执行then方法的同步阶段,因此未执行也就无法确定 Promise 的状态,后续 pending 同理
  3. then((res)=>{console.log(res)}):记作 pres,状态为 pending
  4. 第二个 Promise.reslove():记作 pr2,状态为 fulfilled
  5. then(()=>{console.log(1)}):记作 p1,状态为 pending
  6. then(()=>{console.log(2)}):记作 p2,状态为 pending
  7. then(()=>{console.log(3)}):记作 p3,状态为 pending
  8. then(()=>{console.log(5)}):记作 p5,状态为 pending
  9. then(()=>{console.log(6)}):记作 p6,状态为 pending

图解如下:

image-20240508023258080

step2

此时同步执行完成,只有两个 Promise 状态完成,这两个函数开始执行,首先就会输出 0,然后根据前面分析的规则,如果返回的是一个 Promise,就会调用这个 Promise的then方法,且是放入一个微队列中执行,这里的 Promise.resolve(4) 记作 p4,那么使用一个伪代码表示即为 addMicrotask(p4.then(()=>完成p0)),为什么是完成呢?这里的返回的事 Promise.resolve(),所以在 then 里面是完成 p0,演变图解如下:

image-20240508023559162

step3

现在输出已经可以看到是 0,那么将微队列里面的任务开始执行,p1 的状态也会变为 fulfilled,然后将会输出 1,输出 1 之后

p1 变为 fulfilled 之后就会执行下一个 then 方法里面的成功回调,在这里也就是()=>{console.log(2)},而同理,这个回调也需要加入到微队列,即在 p4.then(()=>完成p0) 后加入

而 p4.then(()=>完成p0),其实需要执行只是()=>完成p0这一部分,then 方法里面的回调会加入到微队列,所以又会向后加入一个微任务,也就是在 ()=>{console.log(2)} 此回调后面,图解如下:

image-20240508023633702

step4

我们继续分析执行结果,执行p4.then(),将里面的回调加入到微队列,那么就会按照顺序执行 console.log(2),p2 变为 fulfilled,紧接着输出 2,输出 2 之后,同理将 ()=>{console.log(3)} 加入到微队列

执行 ()=>完成p0,会将 p0 的状态也变为 fulfilled,p0 变为 fulfilled 之后,就会把 p0 后续的 then(即pres) 加入到微队列,图解如下:

image-20240508024300933

step5

根据结果执行微队列里面的任务,同理 p3 也变为 fulfilled,然后就会输出 3,同时将 p3 下一个 then 方法的回调(即p5)加入到微队列。

然后执行 (res)=>{console.log(res)},同理 pres 状态为 fulfilled,控制台输出 4

res 为什么是 4?Promise a+ 规范,如果返回的是 promise,则下一个 then 方法的执行的回调和结果由这个返回的 Promise 决定,图解如下:

image-20240508025843267

step6

那后续的输出就是一样的了,输出 5,同理 p5 的状态为 fulfilled,将 p6 的回调加入微队列,然后执行,输出 6,完成 p6,最后程序执行结果如图:

image-20240508030258847