理解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 ...
随机推荐
- 快速搭建用于测试的rtsp协议网络流媒体数据服务
背景: 最近根据项目需求,在平台系统中加入了视频监控显示功能,但是限于没有提供真实可用的监控摄像头数据,通过EasyScreenLive快速搭建了一个rtsp的流媒体服务,下面将实现步骤分享给大家,为 ...
- EasyExcel示例(阿里巴巴)基于Maven
首先感谢阿里巴巴提供了easyexcel工具类,github地址:https://github.com/alibaba/easyexcel 注意!!这里只是一个简单的示例,VC大法即可使用,对于复杂的 ...
- css字体效果
text-shadow还没有出现时,大家在网页设计中阴影一般都是用photoshop做成图片,现在有了css3可以直接使用text-shadow属性来指定阴影.这个属性可以有两个作用,产生阴影和模糊主 ...
- Linux系统学习 十八、VSFTP服务—虚拟用户访问—配置虚拟用户访问
配置虚拟用户访问 首先至少要关闭userlist 改完配置文件是要重启服务来使它生效 其实在刚装好vsftp的时候的配置文件不用修改的情况下配置虚拟用户访问控制是最好的 local_root选项不影响 ...
- Python+Unittest+Requests+PyMysql+HTMLReport 接口自动化框架
整体框架使用的是:Python+Unittest+Requests+PyMysql+HTMLReport 多线程并发模式 主要依赖模块 Unittest.Requests.PyMysql.HTMLR ...
- 使用navicat连接只开放内网ip连接的数据库
无法通过Navicat来连接MySQL,比较常见的两种问题? 服务器上自己安装的MySQL数据库,且未开通外网登录账号 直接购买服务商的MySQL数据库不创建公网访问,只有内网访问 背景: 公司数 ...
- OOAD 面向对象的分析与设计
OOAD 面向对象的分析与设计 OOA-----分析阶段(针对业务问题清晰视图, 列出系统完成任务, 整理业务的公共词汇, 列出解决业务的解决方法) O ...
- Element-ui 下拉列表 选项过多时通过自定义搜索来解决卡顿问题
当使用Select选择器时,如果下拉列表的数据量太多,会有一个明显的卡顿体验,例如: <!DOCTYPE html> <html lang="en"> &l ...
- mysql 数据误删恢复
当binlog_format设置为ROW时,想查看binlog中的sql语句时,需要解密,添加这个 --base64-output=decode-rows -v
- [browser navigator 之plugins] 写了一个检测游览器插件
检测IE插件 function hasIEPlugin(name){ try{ new ActiveXObject(name); return true; }catch(ex){ return fal ...