理解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 ...
随机推荐
- C++ map insert 另一个map的子集
C++map中 会有insert操作,举个例子 存在map A,我们截取一部分到map B中,void insert (InputIterator first, InputIterator last) ...
- 50-overlay 如何实现跨主机通信?
上一节我们在 host1 中运行了容器 bbox1,今天将详细讨论 overlay 网络跨主机通信的原理. 在 host2 中运行容器 bbox2: bbox2 IP 为 10.0.0.3,可以直接 ...
- supervisor 工具使用
最近项目要使用supervisor 来管理程序,简单查了查,发现比较容易使用: 中文博客查了查,发现很多人都写出了教程,我这边就懒得写了,找了几个能看懂的记录如下: https://www.cnblo ...
- LVS的工作模式介绍和NAT模式&DR模式实验步骤
一:LVS介绍 二.LVS的NAT和DR模式的实验及配置步骤 一.LVS的简单介绍 linux virtual server 简单来讲lvs是一段内核代码 类似于netfilter本身是一框架但不提供 ...
- subprocess之check_out用法
在python3中使用subprocess的check_out方法时,因为该输出为byte类型,所以如果要查看具体的内容时需要进行转码,如果转码不对话,会影响内容输出的可读性,如下: #1,输出解码不 ...
- 蓝牙spp协议分析
基本概念 蓝牙串口是基于 SPP 协议(Serial Port Profile),能在蓝牙设备之间创建串口进行数据传输的一种设备. 蓝牙串口的目的是针对如何在两个不同设备(通信的两端)上的应用之间保证 ...
- IT兄弟连 HTML5教程 HTML5表单 H5表单提交综合实例
这里我们创建一个填写个人基本信息的表单,使用了表单元素有<input>输入框.<datalist>选项列表.<textarea>文本框,通用的表单输入类型有text ...
- Spring 框架基础(01):核心组件总结,基础环境搭建
本文源码:GitHub·点这里 || GitEE·点这里 一.Spring框架 1.框架简介 Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 ...
- python进程基础点整理
操作系统 串行: 一个程序完完整整的执行完再执行下一个 并发: 看起来像是同时运行,其实就是程序间的切换频率比较快,看不出来 并行:真正的同时运行 多道技术 空间复用:共用一个内存条,多个进程相互隔离 ...
- 每日一题LeetCode 8. 字符串转换整数 (atoi)
问题描述 请你来实现一个 atoi 函数,使其能将字符串转换成整数. 首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止. 当我们寻找到的第一个非空字符为正或者负号时,则将 ...