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 实现细节》https://juejin.cn/post/6844903987183894535#heading-6, 从chrome浏览器实现Promise源码解析为什么then中return 一个Promise对象的时候,会注册两次微任务才会继续注册下一个then

Promise中then的执行顺序详解相关推荐

  1. jmeter接口顺序执行_【接口测试】JMeter中各组件执行顺序详解

    JMeter是我们测试人员使用越来越多的一种测试工具.本文就详细说明一下在JMeter的一个项目内,各个模块和组件的执行顺序是怎么样的. 执行顺序 JMeter常用的各个组件的执行顺序是:配置原件 前 ...

  2. 【异步系列二】Promise原理及执行顺序详解

    前言 Promise 是 javascript 中非常重要的一环,熟悉它是必须的,而且在面试中也常常会问到相关面试题. 在了解 Promise 之前,需要了解什么是异步编程,可以参考我的一篇文章:Ja ...

  3. unity 继承会调用start吗_Unity 继承MonoBehaviour脚本 执行顺序 详解

    先看结果 Awake ->OnEnable-> Start ->-> FixedUpdate-> Update  -> LateUpdate ->OnGUI ...

  4. JS 中关于Promise的用法,状态,执行顺序详解,面试可用(原创)

    前言 在实际项目中我们常会去用已经封装好的promise如axios,或者也会自己去封装promise,甚至在面试中,关于promise的面试题也层出不穷,promise的重要性不言而喻,故写该文章记 ...

  5. python两个装饰器执行顺序_python中多个装饰器的执行顺序详解

    装饰器是程序开发中经常会用到的一个功能,也是python语言开发的基础知识,如果能够在程序中合理的使用装饰器,不仅可以提高开发效率,而且可以让写的代码看上去显的高大上^_^ 使用场景 可以用到装饰器的 ...

  6. python装饰器的顺序_python中多个装饰器的执行顺序详解

    装饰器是程序开发中经常会用到的一个功能,也是python语言开发的基础知识,如果能够在程序中合理的使用装饰器,不仅可以提高开发效率,而且可以让写的代码看上去显的高大上^_^ 使用场景 可以用到装饰器的 ...

  7. Java 中 finally 与 return 的执行顺序详解

    java方法是在栈幀中执行,栈幀是线程私有栈的单位,执行方法的线程会为每一个方法分配一小块栈空间来作为该方法执行时的内存空间,栈幀分为三个区域: 1 . 操作数栈,用来保存正在执行的表达式中的操作数, ...

  8. java try、catch、finally及finally执行顺序详解

    1.为什么要用finally 先看一个没有finally的异常处理try-catch语句: 假设count为要使用到的资源,并且用完要求释放此资源.那么我们可以把释放资源的语句放到try-catch后 ...

  9. Python装饰器执行顺序详解

    探究多个装饰器执行顺序 装饰器是Python用于封装函数或代码的工具,网上可以搜到很多文章可以学习,我在这里要讨论的是多个装饰器执行顺序的一个迷思. 疑问 大部分涉及多个装饰器装饰的函数调用顺序时都会 ...

  10. C++三大继承构造函数的执行顺序详解

    写的挺好的,关于继承和构造函数的先后顺序问题. 转自: http://blog.csdn.net/daheiantian/archive/2011/02/18/6438782.aspx 一.单继承 核 ...

最新文章

  1. 机器学习分类指标:精确率、准确率、召回率详解
  2. char* 和jstring转换
  3. XSS盗COOKIE
  4. 10kv电压互感器型号_10kv电流互感器的一次电流选择应遵循的基本原则
  5. [GCN] 代码解析 of GitHub:Semi-supervised classification with graph convolutional networks
  6. android小程序:计算圆面积
  7. 利用Python进行数据分析笔记-时间序列(时区、周期、频率)
  8. 实习踩坑之路:实习以来获得的踩坑经验、一些提升开发效率的方法或者常用技巧
  9. InnoDB Plugin 1.0.2 for MySQL 5.1.30 (GA) Released
  10. 猫眼电影排行榜前100爬取案例学习笔记
  11. 如何理解IT、OT、CT?
  12. 免费域名邮箱申请教程
  13. 订单流失总金额怎么算_微信新规下增长乏力,裂变到底该怎么做?
  14. 微信公众号起名字攻略:怎么给公众号取名字?
  15. 不输给MacBook的win10精确式触摸板
  16. python代码风格程序越复杂越高级_Python中代码风格的改变和相应的性能优化
  17. 计算机面试专业英语词汇,面试常用英语,英语面试常用词汇?
  18. 网安渗透安服 电脑 环境 配置 一文全
  19. 二次型(Quadratic Form)
  20. java就业率高吗_java好就业吗

热门文章

  1. 【122天】尚学堂高琪Java300集视频精华笔记(43-46)
  2. java 图书馆系统_JAVA 图书馆管理系统
  3. cf Educational Codeforces Round 47 E. Intercity Travelling
  4. 点评互联网产品经理面试问题汇总(18问)
  5. 计算机术语死机,计算机“死机”故障原因及处理办法大全
  6. Linux的开源操作系统
  7. 武昌理工学院计算机巧业怎样,最潮高校宿管员巧念育人经 做学生们的“知心大姐”...
  8. HTML+CSS学习打卡第一周
  9. Java数组的复制、扩容、删除
  10. 用Excel做了7天报表,这个领导喜欢的可视化工具,只用了7小时