理解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 ...
随机推荐
- [PHP] php使用event扩展的io复用测试
先要安装event扩展,这样才可以使用libevent的事件机制 pecl install event 测试代码 //连接重用 //创建资源流的上下文 $context=stream_context_ ...
- mysql分组报错Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column
环境:Server version: 5.7.27-0 ubuntu 18.04.1 (Ubuntu) 执行分组语句:select * from pet group by owner;时报错: mys ...
- 关于input标签不同type下的盒模型
刚才发现,在Chrome下input标签的不同type类型所取的盒模型是不一样的.浪费了我很多时间去调试,唉. type="text"时,给它设置宽度width:300px,此时的 ...
- 【搬了一套别人的cf】
自己打了一堆没保存瞬间全没了.... 没有继续写的欲望 https://www.cnblogs.com/tea-egg/p/11664350.html
- Python的标准库介绍与常用的第三方库
Python的标准库介绍与常用的第三方库 Python的标准库: datetime:为日期和时间的处理提供了简单和复杂的方法. zlib:以下模块直接支持通用的数据打包和压缩格式:zlib,gzip, ...
- 在CV尤其是CNN领域的一些想法
现在的CNN还差很多,未来满是变数. 你看,现在的应用领域也无非merely就这么几类----分类识别,目标检测(定位+识别),对象分割......,但是人的视觉可不仅仅这么几个功能啊!是吧. 先说说 ...
- ASP.NET MVC教程三:ASP.NET MVC部署方式
ASP.NET MVC编写的程序需要部署到IIS上面才能进行访问,部署方式分为两种. 一.直接用源代码部署 第一种方式可以直接使用源代码进行部署.部署步骤: 1.新建网站 在IIS里面选择网站,然后右 ...
- 使用dom4j类操作xml文档
dom4j操作xml数据 1.Document对象相关 ①读取XML文件,获得document对象. SAXReader reader = new SAXReader(); Document docu ...
- git命令教程
git教程笔记 Git是什么? Git是一个分布式版本控制系统 版本控制方式 集中式版本控制:从版本库中先取得最新的版本,改完之后再上传到版本库中,需要联网 分布式版本控制:每个合作者电脑上都有一个版 ...
- Java开发人员必备十大工具
Java世界中存在着很多工具,从著名的IDE(例如Eclipse,NetBeans和IntelliJ IDEA)到JVM profiling和监视工具(例如JConsole,VisualVM,Ecli ...