Promise中then的执行顺序详解

Promise在前端中主要用于处理异步调用,其基本使用方式通过阮一峰大佬的文档一下就可以入手,但是最近我看了一篇文章wecTeam中,作者深山蚂蚁的《高级进阶:深度揭秘Promise注册微任务和执行过程》一文,让我对Promise的执行顺序有了更深的了解,与此同时我也有了一个疑问,通过这篇文章与大家探讨。

1. promise的异步主要发生在微任务队列中

2. 第一个then的回调监听最新Promise对象的resolve执行后才xx注册进微任务队列,之后的then回调都依赖于前一个then中的代码执行结束。

下面的内容主要基于两个概念讨论:

 (1) 当前一个then中的代码都是同步执行的,执行结束后第二个then即可注册进入微任务队列。

 (2) 当前一个then中有return 关键字,需要return的内容完全执行结束,第二个then才会注册进入微任务队列。

then也分下面几种情况:

// Case 1:  前一个then中的代码都是同步的
new Promise( (resolve) => { resolve();})
.then(() => {console.log(1);})
.then(() => {console.log(2)});



// 输出 
//  1
//  2 



// Case 2: 前一个then中的代码 return一个Promise对象
new Promise((r,rj) => {
  console.log('外p');
  r();
}).then(() => {
  console.log('外then1');
  new Promise(((r,rj) => {
    console.log('内p');
    r();
  })).then(() => {
    console.log('内then1');
    return new Promise((r, rj) => {r();});
  }).then(() => {
    console.log('内then2');
  });
}).then(() => {
  console.log('外then2');
}).then(() => {
  console.log('外then3');
}).then(() => {
  console.log('外then4');
});

/**
   输出结果:
    "外p"
    "外then1"
    "内p"
    "内then1"
    "外then2"
    "外then3"
    "外then4"
    "内then2"
**/

我自己疑惑的主要是Case2这种情况,为什么"内then2"会在"外then4"之后打印?

    return new Promise((r, rj) => {r();})  等同于

    return new Promise((r, rj) => {r();}).then(()=>{console.log(1)}).then(()=>{console.log(2)}).then(()=>{console.log(3)});

这里面究竟藏着什么原因?

为什么Part one和Part two的打印结果是一样的?

-------------  Part one ------------------------
new Promise(r => {
  r(1)
}).then(r => {
  console.log('p1 then1');
  return new Promise(r => {
    r(2);
  });
}).then(r => {
  console.log('p1 then2', r);
});


new Promise(r => {
  r(1)
}).then(r => {
  console.log('p2 then1');
}).then(r => {
  console.log('p2 then2');
}).then(r => {
  console.log('p2 then3');
}).then(r => {
  console.log('p2 then4');
});


-------------  Part two ------------------------

new Promise(r => {
  r(1)
}).then(r => {
  console.log('p1 then1');
  return new Promise(r => {
    r(2);
  }).then(r => r);
}).then(r => {
  console.log('p1 then2', r);
});

new Promise(r => {
  r(1)
}).then(r => {
  console.log('p2 then1');
}).then(r => {
  console.log('p2 then2');
}).then(r => {
  console.log('p2 then3');
}).then(r => {
  console.log('p2 then4');
});



最新在研究Promise实现原理,弄清了then方法的代码实现后,对于以上今年年初记录这篇博客遗留的问题有了比较清晰的解释了。

废话不多说,直接上代码,下图就是then方法返回一个promise的对象时,会有一次空的微任务执行的“错觉”的原因

// 简化版then的实现
Promise.prototype.then = function(onFullfilled, onRejected) {
    onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : function (data) { return data; };
    onRejected = typeof onRejected === 'function' ? onRejected : function (reason) { return reason; };

    const self = this;
    const promise2 = new MyPromise(function (resolve, reject) {
        if (self.status === PENDING) {
            self.resolveCallbacks.push(function () {
                // 此处的setTimeout用来模拟将该处代码推入微任务队列
                setTimeout(function() {
                    const x = onFullfilled(self.value);
                    resolvePromise(promise2, x, resolve, reject);
                }, 0);
            });

            self.rejectCallbacks.push(function () {
                setTimeout(function() {
                    const x = onRejected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                }, 0);
            })
        }

        if (self.status === RESOLVED) {
            setTimeout(function() {
                const x = onFullfilled(self.value);
                resolvePromise(promise2, x, resolve, reject);
            }, 0);
        }

        if (self.status === REJECTED) {
            setTimeout(function() {
                const x = onRejected(self.reason);
                resolvePromise(promise2, x, resolve, reject);
            }, 0);
        }
    });

    return promise2;
}


function resolvePromise (promise2, x, resolve, reject) { 
    if (promise2 === x) { 
      // 防止自己等待自己 
      return reject(new TypeError('循环引用了')); 
    } 
    let called; // 表示Promise有没有被调用过 // x是object或者是个function 
    if ((x !== null && typeof x === 'object') || typeof x === 'function') {
     try { 
         let then = x.then; 
         // 如果返回的x是promise对象, 执行这个promise的then方法,递归resolvePromise将第二个    
         // 参数为非thenable为止
         if (typeof then === 'function') { 
             then.call(x, function (y) { 
                 if (called) { 
                     // 是否调用过 
                     return; 
                 } 
                 called = true; 
                 resolvePromise (promise2, y, resolve, reject) 
             }, 
             function (r) { 
                if (called) { 
                   // 是否调用过 
                   return; 
                } 
                called = true; 
                reject(r); 
             }); 
         }   
         else { 
         // 当前then是一个普通对象。 
             resolve(x) 
         }
      } 
      catch (e) { 
          if (called) { 
               // 是否调用过 
               return; 
           } 
           called = true; 
           reject(e); 
           } 
      } 
      else { 
           if (called) { 
           // 是否调用过 
           return; 
           } 
           called = true; 
           resolve(x); 
      } 
}

我们结合一个例子来看

const p = new Promise(function(rs, rj) { rs(1); }) .then(r1 => {
    console.log('r1', r1);
    return new Promise(function(rs2, rj2){
        rs2(2)     
    });

});

p.then(r2 => {
    console.log('r2', r2);
})


new Promise(r => r(11))
.then(r11 => {
  console.log('r11', r11);
  return r11 + 1;
})
.then(r12 => {
  console.log('r12', r12);
  return r12 + 1;
})
.then(r13 => {
  console.log('r13', r13);
});

深度的原因,推荐掘金大佬文章《【V8源码补充篇】从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节》深度揭秘 Promise 微任务注册和执行过程 - 掘金, 从chrome浏览器实现Promise源码解析为什么then中return 一个Promise对象的时候,会注册两次微任务才会继续注册下一个then

  • 9
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值