理解Promise.all,Promise.all与Promise.race的区别,如何让Promise.all在rejected失败后依然返回resolved成功结果
壹 ❀ 引
我在 es6入门4--promise详解 这篇文章中有详细介绍Promise对象的用法,文章主题更偏向于对于Promise概念的理解与各方法基本使用介绍;而世上一个比较有趣的问题就是,即便按照前人提供的规则与方法去做一件事,也会因为未知的缘故产生新问题,这让人非常苦恼,但大多数情况都是因为自身理解不深刻导致;在昨天的工作中使用Promise.all的经历也是把我整的不轻(最后查出来是后台逻辑BUG...),这也是我想另起一篇文章专门介绍Promise.all与Promise.race的原因。
那么本文主要围绕三个观点展开,一是重新认识Promise.all与Promise.race,二是理解而二者有本质区别,三是在all方法失败的情况下如何依然获取resolve的状态,那么本文开始。
贰 ❀ Promise.all
Promise.all用于将多个Promise实例包装成一个新的Promise实例,我们来看个例子:
var p1 = Promise.resolve(1);
var p2 = Promise.resolve(2);
var p = Promise.all([p1,p2]);
console.log(p);

上述例子中新Promise实例 p 的回调结果受p1 p2影响,即如果p1,p2都resolved,p的成功回调会执行,并返回一个包含p1 p2 resolved结果的数组。
p.then(function (resp) {
console.log(resp);//[1,2]
})

但如果p1,p2其中任意一个rejected,p的失败回调会执行,并返回第一个失败的结果,成功回调不执行。
var p1 = Promise.resolve(1);
var p2 = Promise.reject(2);
Promise.all([p1, p2])
.then(function (resp) {
console.log(resp); //不会执行
}).catch(function (err) {
console.log(err); //
});
需要注意的是,如果Promise.all()的参数不是Promise实例,那么在Promise.all的回调执行会先调用Promise.resolve方法(或者reject方法)将这些参数转为Promise实例,更有趣的是,转过执行虽然符合JS执行机制先后顺序,但转变完成后all的成功回调拿到的数组结果顺序,永远与Promise参数顺序一致,我们来看个例子:
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
console.log(1);//后执行
}, 4000);
})
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
console.log(2);//先执行
}, 1000);
})
Promise.all([p1, p2])
.then(function (resp) {
console.log(resp); //[1,2]
})
在上述例子中,由于p1,p2不是一个Promise实例,所以在all回调前还得执行resolve方法,由于两者定时器等待时间不同,所以时间短的先执行,但即便如此,all回调的结果,p1的结果依然在前:

叁 ❀ Promise.all与Promise.race的区别
Promise.race同样是将多个Promise实例包装成一个新的Promise实例,但新实例的执行结果与第一个先改变状态的Promise状态保持一致,即如果第一个Promise实例为rejected,那么新实例也为rejected,反之亦如此。
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
console.log(1); // 第三个执行
}, 4000);
})
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2);
console.log(2); //先执行
}, 1000);
})
Promise.race([p1, p2])
.then((resp) => {
console.log(resp);//不执行
}).catch((err) => {
console.log(err);//2 第二个执行
})
在上述例子中,由于p2先执行,状态为rejected,导致race的catch方法执行执行,拿到错误的响应。
那么到这里我们大概知道了两者的不同,Promise.all就像执行任务的刺客团队,要么全部成功才算成功,要么一个失败直接失败,非常讲究团队精神。而Promise.race的结果只参照第一个改变状态的Promise,第一个为成功它就成功,第一个失败它就跟着失败,非常冷酷无情。
其次,Promise.all的resolveed结果顺序与参数顺序完全一致,即便第一个参数改变状态在后,但它的结果依旧在前,Promise.all与Promise.race转变参数状态的顺序都符合JS执行机制,以定时器为例,时间小的永远先改变状态。
肆 ❀ Promise.all在reject后依旧返回resolve
在上文中,我们知道Promise.all在处理多个Promise实例时,如果一个失败,就只能拿到第一个失败的结果,其余成功的结果都无法拿到,那有什么办法能在reject后依旧拿到所有执行结果呢?有,利用catch方法。
首先我们需要知道Promise的状态具有可传递性,其次catch方法在执行后也会返回一个状态为resolved的新Promise实例,所以我们只要将可能reject的Promise实例先catch一遍就可以了,就像做一次状态预加工:
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2);
}, 1000);
});
var promiseArr = [p1, p2];
var promiseArr_ = promiseArr.map(function (promiseItem) {
return promiseItem.catch(function (err) {
return err;
})
});
Promise.all(promiseArr_)
.then((resp) => {
console.log(resp);//[1,2]
}).catch((err) => {
console.log(err);
});
伍 ❀ 总
那么到这里,我们重新介绍了Promise.all与Promise.race这两个方法,通过本文你也知道了all的特点是要么参数全部成功则自己成功,要么某个失败则自己失败,而race只跟随第一个改变状态的Promise执行对应回调;其次我们还利用了catch方法完善了Promise.all方法,即便有参数rejected,我们依旧能拿到完整的响应结果。那么到这里本文结束。
参考
理解Promise.all,Promise.all与Promise.race的区别,如何让Promise.all在rejected失败后依然返回resolved成功结果的更多相关文章
- Promise的三兄弟:all(), race()以及allSettled()
摘要: 玩转Promise. 原文:Promise 中的三兄弟 .all(), .race(), .allSettled() 译者:前端小智 Fundebug经授权转载,版权归原作者所有. 从ES6 ...
- 从tcp原理角度理解Broken pipe和Connection reset by peer的区别
从tcp原理角度理解Broken pipe和Connection reset by peer的区别 http://lovestblog.cn/blog/2014/05/20/tcp-broken-pi ...
- 连续张量理解和contiguous()方法使用,view和reshape的区别
连续张量理解和contiguous()方法使用,view和reshape的区别 待办 内存共享: 下边的x内存布局是从0开始的,y内存布局,不是从0开始的张量 For example: when yo ...
- IL角度理解C#中字段,属性与方法的区别
IL角度理解C#中字段,属性与方法的区别 1.字段,属性与方法的区别 字段的本质是变量,直接在类或者结构体中声明.类或者结构体中会有实例字段,静态字段等(静态字段可实现内存共享功能,比如数学上的pi就 ...
- promise对象的回调函数resolve的参数为另一个promise对象
/*如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数. reject函数的参数通常是Error对象的实例,表示抛出的错误: resolve函数的参数除了正常的值 ...
- 为Promise添加finally方法支持,把小程序函数变成promise函数
// 为Promise添加finally方法支持 Promise.prototype.finally = function (callback) { let P = this.constructo ...
- 深入理解QStateMachine与QEventLoop事件循环的联系与区别
最近一直在倒腾事件循环的东西,通过查看Qt源码多少还是有点心得体会,在这里记录下和大家分享.总之,对于QStateMachine状态机本身来说,需要有QEventLoop::exec()的驱动才能支持 ...
- 12月21日 简单理解Active Recore Callback, destroy_all和delete_all的区别。豆知识(alias),语言学习法(4核心)
destroy_all and delete_all Destroy the records by instantiating each record and calling its #destroy ...
- 【JavaScript】深入理解call,以及与apply、bind的区别
一.call call有两个妙用 1.继承(我前面的文章有提到用call实现call继承,有兴趣可以看下.https://www.cnblogs.com/pengshengguang/p/105476 ...
随机推荐
- 数据库迁移导致Unknown character set: 'GBK' 应用异常
https://blog.csdn.net/u013415591/article/details/82692242https://blog.csdn.net/gx_1_11_real/article/ ...
- python判断文件夹和文件是否存在
1.os.path.exists()既可以判断文件是否存在,又可以判断文件夹是否存在 2.os.path.isfile()判断文件是否存在 3.os.path.isdir()判断文件夹是否存在
- InnoDB On-Disk Structures(三)--Tablespaces (转载)
转载.节选于 https://dev.mysql.com/doc/refman/8.0/en/innodb-tablespace.html This section covers topics rel ...
- August 18th, 2019. Week 34th, Sunday
Fear doesn't shut you down, it wakes you up. 恐惧不会消磨你的意志,它能激发你的潜能. We all know that fear is powerful, ...
- 201871010113-刘兴瑞《面向对象程序设计(java)》第十七周学习总结
项目 内容 这个作业属于哪个课程 <任课教师博客主页链接>https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址>http ...
- 2019-2020-1 20199305《Linux内核原理与分析》第九周作业
进程的切换和一般执行过程 (一)进程调度的时机 (1)关键问题 何为进程切换?就是进程调度时机到来时从就绪进程队列中能够挑选一个进程执行,占用CPU时间,那么就有两个关键问题:一是什么时间去挑选一个就 ...
- swoole怎么保持不掉线
正常情况下客户端中断TCP连接时,会发送一个FIN包,进行4次断开握手来通知服务器.但一些异常情况下,如客户端突然断电断网或者网络异常,服务器可能无法得知客户端已断开连接. 尤其是移动网络,TCP连接 ...
- 深入浅出14个Java并发容器
前言 不考虑多线程并发的情况下,容器类一般使用ArrayList.HashMap等线程不安全的类,效率更高.在并发场景下,常会用到ConcurrentHashMap.ArrayBlockingQueu ...
- iOS的常用类库
target 'NewCompass' do #UI通用 pod 'SVProgressHUD' pod 'MJRefresh' pod 'SnapKit' #pod 'RTRootNavigatio ...
- 前端之JavaScript基础及使用方法
JavaScript概述 ECMAScript和JavaScript的关系 1996年11月,JavaScript的创造者--Netscape公司,决定将JavaScript提交给国际标准化组织ECM ...