Promise.resolve的参数是thenable对象时

http://es6.ruanyifeng.com/#do...
这个是阮一峰老师关于Promise.resolve()参数的解读,其中参数是thenable对象时,Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

问题来了:

new Promise(resolve => {

resolve(1);
Promise.resolve({
    then: function(resolve, reject){
        console.log(2);
        resolve(3)
    }
}).then(t => console.log(t))
console.log(4);

}).then(t => console.log(t));
console.log(5);

控制台输出:4 5 2 1 3

下面按照最开始的理解,将上述代码进行转换
new Promise(resolve => {

resolve(1);
new Promise(resolve=>{
    console.log(2);
    resolve(3)
}).then((t) => console.log(t));
console.log(4);

}).then(t => console.log(t));
console.log(5);

控制台输出 2 4 5 3 1

顺序不一样。。我很崩溃 求解答

阅读 7k
3 个回答

你要理解两点,

  1. Promise 的 resolve 是把任务往一个栈里压,这个栈里的任务根据 Promise 规范实现的不同(如原生和 polyfill)有的会在当前 event loop 结束前清空(micro-task),有的会在当前 event loop 完了之后清空(macro-task),总之就是会在平台代码的后面。
  2. 这个平台代码是指:

    Here “platform code” means engine, environment, and promise implementation code.

很多人就是忽略了 promise 实现的代码也是算做平台代码的。

那么看回例子,

  1. new Promise([executor]) 首先执行,进入 executor 函数。
  2. 第一行就已经 resolve(1); 这个 Promise 马上由 pending 状态过渡到 fulfill 状态。这个带 1 的任务被压到前面提到的栈中。
  3. 继续执行 executor 剩下的代码。Promise.resolve([object]),这个带 [object] 的任务被压到栈中。
  4. 遇到第一个 console.log(4);
  5. 第二个 console.log(5);
  6. 本轮的平台代码运行完毕,开始清栈。
  7. 处理带 [object] 的任务,因为这个 object 中有 then 方法,直接当新的 executor 调用。
  8. 注意 executor 是同步的,这里马上遇到 console.log(2)
  9. 接着 resolve(3),因为本轮的栈还没处理完(Promise 底层也属于“平台代码”),这个带 3 的任务被压到下一轮的栈中。
  10. 处理带 1 的任务,.then(t => console.log(t)); 打印 1
  11. 栈空,马上清理下一轮的栈。
  12. 打印 3
  13. 处理完毕。

楼主关于Promise.resolve()参数的解读其实没错(Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。),唯一忽略的传入参数的对象已经转为Promise了,然后调用该对象的then方法,就已经是异步操作也就是微任务。

可以测试一下代码

        Promise.resolve({
            then: function(resolve, reject){
                console.log(2);
                resolve(3)
            }
        })
        .then(t => console.log(t))

        console.log(4);

如果调用该对象的then方法是同步的,输出应该是 2 4 3,可是实际输出也就是 4 2 3
有了这个前提后我把代码重新标注,用楼主你的思维来解释一下。(@CRIMX的解释太专业了,我按照我个人理解来解析,求轻喷)
标注代码如下:

   new Promise(resolve => { //1 promise构造函数的参数函数

        resolve(1); //2

        Promise.resolve({  //3  Promise.resolve 
            then: function(resolve, reject){  //4 内then函数 
                console.log(2);
                resolve(3) //8
            }
        })
        .then(t => console.log(t))   //9 内then1函数 

        console.log(4);  // 5 
    })
   .then(t => console.log(t));  //6  外then函数

    console.log(5);  // 7

1.代码运行,开始运行promise构造函数的参数函数(同步函数)
2.运行到resolve(1),promise实例状态变为fulfill,这时候因为该函数是同步函数,后面还有未执行的代码,所以还不会把第6步的外then函数加入微任务,当同步函数(第1步)执行完毕后才会加入微任务。
3.运行到Promise.resolve([object]),转为promise对象,然后运行对象then函数(内部then函数)
4.运行内部then函数,添加到微任务,然后继续同步函数,遇见第9步的内then1函数 ,该then函数会等到第3步的promise决议后才执行,所以该函数会保留在promise中
5.输出4
6.此时同步函数运行完毕,遇见外部then函数,因为外部promise已经决议(步骤2),所以该函数也加入微任务队列,现在微任务队列为【内部then函数,外部then函数】
7.继续执行其他同步代码,输出5
8.同步代码执行完毕,执行微任务,取出内部then函数,输出2.然后resolve(3)(决议该promise),执行之前没有调用的内then1函数。
9.运行内then1函数,加入微任务队列,现在微任务队列为【外部then函数,内then1函数】,该微任务完成
10.依次调用微任务,输出1,输出3。

  1. new Promise()接收一个函数参数,这个函数是立即执行的,即同步。
  2. Promise的then方法都是异步的
  3. Promise.resolve()接收thenable参数,调用thenable的then方法也是异步的。

你可以去看下一些实现promise的库的源码,会更清晰。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏