Advertisement

一道promise的题

阅读量:

该问题是本人在2021.5.28一个交流群里看到的
----------2021.6.27 在知乎里也看到了这个问题知乎链接,首赞解释得比较清楚,下面是当时的探索历程,这句话是一个月后加的


问题

复制代码
        Promise.resolve().then(() => {
            console.log(0);
            return Promise.resolve(4);
        }).then((res) => {
            console.log(res)
        })
    
        Promise.resolve().then(() => {
            console.log(1);
        }).then(() => {
            console.log(2);
        }).then(() => {
            console.log(3);
        }).then(() => {
            console.log(5);
        }).then(() => {
            console.log(6);
        }).then(() => {
            console.log(7);
        })
    
    
    javascript
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/6YqmbPren4LtWBQNgJFZCyxOKAs8.png)

答案:在这里插入图片描述
得彻底搞清楚promise的原理才行
在这里插入图片描述

探寻历程

–时间 2021.6.22重新复习promise源码,依旧没有得到如何来的答案
思路如下

复制代码
    then1内部回调,因为前面是给的成功的p对象,所以会放入微队列
    
    then2因为then1返回的是pending的p1对象,所以会把回调放到p1的callbacks数组里,
    等待then1回调出结果后,then1内部根据结果执行resolve(v)或者reject(r),
    在resolve(v)或者reject(r)内部,会把处理callbacks操作加入微队列
    
    第二段开始,除了then3会得到一个成功的p对象,后面的then都只能得到一个pending的对象
    所以执行同步代码的时候,
    只有then3的回调会主动放到微队列,然后把后面的then都放到前面对应的p对象的callbacks里
    
    同步结束,只有then1和then3的回调以此进入微队列,其余均是callbacks里
    
    第一次微队列,先执行then1的回调, 输出0, 然后得到一个成功的值为4 的p_success(自己瞎命名的)对象
    then1内部得到成功的p对象,执行resolve(p_success),把放有 then2 的回调的callbacks处理函数放入新的微队列
    
    then2,已经没啥事了,同步异步均跟他无关
    
    跳到then3的回调, 执行, 输出1 , 然后得到一个成功的,值为undefined的 p对象
    then3内部,得到成功p对象,执行resolve(p),把放有 then4 的回调的callbacks处理函数放入新的微队列
    
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/p3qhyeJzGAdxYvt4EZWRMCgjHNQ2.png)

到这里就卡住了,不知道为什么输出1后会继续输出2然后3,又跳到4,明明输出0后微队列里面就只有输出1,输出0后把输出4的回调放入了微队列,但是输出1后却不是执行的输出4,而是输出2,

太难了,而且,源码的教学基本上对于promise都是用setTimeout来模拟,我用queueMicrotask来模拟也没办法达到原版promise的效果,但是明确了一点

复制代码
    通过手写promise,知道了 new Promise()的时候,并不是异步执行的,
    Promise.resolve()也不是异步执行的,所有关于Promise异步执行的只是then里的回调函数即参数
    并且,then方法也不全是异步,只有里面的执行回调的部分是异步的,返回一个新Promise的代步不是异步的
    但是在异步执行完毕之前,该promise的状态是pending,通过闭包等待异步执行完毕后改变状态
    
    

promise里面的代码,只有then里的回调是异步的,其他都是同步的,当异步处理的时候,有可能then没有得到结果来执行回调,就会保存回调到实例对象上,但等到闭包拿到结果后,也会是加入异步队列去处理这个回调函数,

看到一篇文章,里面说then的注册只会在上一个then返回来一个有状态的promise对象,如果再往下继续then,但是没有结果,就会去执行其他同步代码, 文章链接
在这里插入图片描述
但是我持保留意见,因为我在debug的时候,把每个then都let一个对象,可以看到每个对象其实都是生成了的,只不过都是pending状态,如下

复制代码
    let p1 = Promise
    .resolve()
    
    let p2 = p1.then(() => {
    console.log(0);
    return p = Promise
        .resolve(4);
    })
    let p3 = p2.then((res) => {
    console.log(res)
    })
    
    let p4 = Promise
    .resolve()
    let p5 = p4.then(() => {
    console.log(1);
    })
    let p6 = p5.then(() => {
    console.log(2);
    })
    let p7 = p6.then(() => {
    console.log(3);
    })
    let p8 = p7.then(() => {
    console.log(5);
    })
    let p9 = p8.then(() => {
    console.log(6);
    })
    let p91 = p9.then(() => {
    console.log(7);
    })
    
    
    javascript
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/LRncg0Vao3sxvw58YlhkrmADFMuQ.png)

在这里插入图片描述 在这里插入图片描述

先保留吧,确实做不出来目前,想了两天了

重要参考

-----2021.6.27,在b站教学视频里找到了答案,链接如下
饥人谷前端promise教学视频2p 13分钟左右
在这里插入图片描述
如图,视频中方老师说then方法是黑盒,最好不要去探究原理,因为promise实现者随时都能改掉内部规则,就像node11以下,微任务不支持插队,微任务1里有微任务3,那么3也会排在微任务2后面再进行,但是后来改了规则,微任务可以立即插队立即解决, 他的意思是只要记住规则就好,而且promiseA+规范里面也没写这个。(其实我本人不太赞同这个观点,我还是倾向于了解内部原理,这样对自己提升很大,当然方老师是站在教育培训者角度,面对学员当然是希望记住就行,先把面试通过都好说)

言归正传

那我就跟着方老师思路走先,在这种普通的链式调用情况下,上下两个promise的回调执行顺序是一上一下交替进行的

然后加入一个 return Promise.resolve(3.5) ,就会改变顺序,他说原理他也不知道,记住规律就行,
规律就是:原本是一上一下交替进行,现在上面加了一个return 一个Promise.resolve()操作,上面那个promise的下一个回调就会在原本该执行的地方推迟两个回调后执行,比如在图中3里面加了return Promise.resolve(),下一个5本来该是回调4执行后就执行,现在变成4执行后再等两个,这里5都得等,自然就是执行下面promise的两个后,也就是6、8之后执行5,然后接着上下交替
在这里插入图片描述
所以顺序由12345678910变成了1234 > 6 >8 >5 >10 >7 >9

后面验证又加了一个return Promise.resolve(),同样在7后面本该是 7 > 12 >9 >14 >16 变成 7 >12 >14 >16 >9
在这里插入图片描述
但是注意,这种情况适用于函数返回经过Promise.resolve()后的结果,如果只是写一个return ,而不是return Promise.resolve(),就不能适用这种规律,所以看下面的代码

复制代码
        Promise.resolve().then(() => {
            console.log(0);
            return 
        }).then((res) => {
            console.log(res)
        })
    
        Promise.resolve().then(() => {
            console.log(1);
        }).then(() => {
            console.log(2);
        }).then(() => {
            console.log(3);
        }).then(() => {
            console.log(5);
        }).then(() => {
            console.log(6);
        }).then(() => {
            console.log(7);
        })
    
    
    javascript
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/GmDApBiyJqbYw6nh5WVROPN3ZKsg.png)

会输出在这里插入图片描述
因为只是一个return,是无法适用刚刚那个规律的,依然会一上一下

同样,在下面的例子里,利用resolve向外传递,也是可以适用的,类似于return 一个结果,因为promise的原理就是向外返回一个promise对象,return是根据后面的对象类别,而resolve则是返回一个成功的promise对象而已

复制代码
        new Promise((resolve, reject) => {
            console.log(0);
            resolve(Promise.resolve())
        }).then(() => {
            console.log(4);
        })
    
        Promise.resolve().then(() => {
            console.log(1);
        }).then(() => {
            console.log(2);
        }).then(() => {
            console.log(3);
        }).then(() => {
            console.log(5);
        }).then(() => {
            console.log(6);
        }).then(() => {
            console.log(7);
        })
    
    
    javascript
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/QfKoYbslyG2JaW9d0gUASwH6T83k.png)
在这里插入图片描述

规律总结

即是 then里面返回一个 Promise.resolve() ,
或者 new Promise的excutor同步代码里resolve( Promise.resolve ( ) ),当然reject就直接失败了,会穿透,一般不考虑,成功的才会链式顺序调用
只要是在 能向外返回一个成功的promise对象的代码块里,比如再写一个 Promise.resolve(),这个 Promise.resolve()操作就会阻隔后面挨着的两次回调,之后再依次进行,或者直接 new Promise(excutor) , 在excutor里面resolve(value),也是一样的会阻隔两次

如下,我把

复制代码
    Promise.resolve().then(() => {
        console.log(0);
        return Promise.resolve(4);
    }).then((res) => {
        console.log(res)
    })
    
    

改成了

复制代码
      return new Promise((resolve, reject) => {
    resolve(4)
      })
    
    

依然是输出01234567

复制代码
    Promise.resolve().then(() => {
      console.log(0);
      return new Promise((resolve, reject) => {
        resolve(4)
      })
    }).then((res) => {
      console.log(res)
    })
    
    Promise.resolve().then(() => {
      console.log(1);
    }).then(() => {
      console.log(2);
    }).then(() => {
      console.log(3);
    }).then(() => {
      console.log(5);
    }).then(() => {
      console.log(6);
    }).then(() => {
      console.log(7);
    })```
    
    # 答案
    视频中看到知乎的答案,这个不只是根据规律了,而是根据原理,知道了promise的resolve的逻辑,知道了为什么是这样的[知乎](https://www.zhihu.com/question/430549238/answer/1624864911)
![在这里插入图片描述]()
    **总结** **resolve 的参数为 Promise 时,要额外消耗两个 microtask,第一个 microtask 是规范要求的 PromiseResolveThenableJob;第二个 microtask 的功能是把 resolvedPromise 的状态同步给 p0。**
    
    首赞的大佬写的很详细[知乎链接](https://www.zhihu.com/question/430549238/answer/1624864911)
![在这里插入图片描述]()
    但是其实源码也是有可能变的,在node6 7 8 10 11等版本都是这个输出,但是node9就是挨着输出,视频里方老师讲的还是有一定道理的
    
    # 回顾本题
    所以可以知道这道题的答案了
    没有return 的情况下
    ```javascript
        Promise.resolve().then(() => {
            console.log(0);
        }).then((res) => {
            console.log(res)
        })
    
        Promise.resolve().then(() => {
            console.log(1);
        }).then(() => {
            console.log(2);
        }).then(() => {
            console.log(3);
        }).then(() => {
            console.log(5);
        }).then(() => {
            console.log(6);
        }).then(() => {
            console.log(7);
        })
    
    
    javascript
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/zCT9ZPMrpqB3f8uicg5SvWEojH0b.png)

答案:0 1 undefined 2 3 5 6 7
在这里插入图片描述
加上之后就把 undefined的位置变为4并且 在1 后卡两个回调 变成 0 1 2 3 4(原undefined) 5 6 7

复制代码
        Promise.resolve().then(() => {
            console.log(0);
            return Promise.resolve(4);
        }).then((res) => {
            console.log(res)
        })
    
        Promise.resolve().then(() => {
            console.log(1);
        }).then(() => {
            console.log(2);
        }).then(() => {
            console.log(3);
        }).then(() => {
            console.log(5);
        }).then(() => {
            console.log(6);
        }).then(() => {
            console.log(7);
        })
    
    
    javascript
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/25ecHRMKwEn0PrNkpl9jBv4TqJ6A.png)

全部评论 (0)

还没有任何评论哟~