理解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); //2 });
需要注意的是,如果Promise.all()的参数状态未确定未确定,那么在Promise.all的回调执行会先调用Promise.resolve方法(或者reject方法)将这些pending状态的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,我们依旧能拿到完整的响应结果。那么到这里本文结束。
参考
相关文章
- 学妹问我: 如何提高编程能力
- 如何在 PHP 中处理 Protocol Buffers 数据
- 请求大神,C#如何截取字符串中指定字符之间的部分 按指定字符串分割 一分为二 c# 去除字符串中的某个已知字符
- 基于 Vue.js 之 iView UI 框架非工程化实践记要 使用 Newtonsoft.Json 操作 JSON 字符串 基于.net core实现项目自动编译、并生成nuget包 webpack + vue 在dev和production模式下的小小区别 这样入门asp.net core 之 静态文件 这样入门asp.net core,如何
- 如何在jmeter中把响应中的数据提取出来并引用
- UE4读取脑电波MindWave插件(展示如何使用第三方库制作UE4插件)
- 云服务器、虚拟主机以及服务器如何定义的?三者有什么区别?
- 测试/开发程序员面试如何谈薪资,小技巧......
- 如何设置一个永远无法删除的Cookie
- 如何在 Ubuntu 13.10 中安装 Gnome 3.10
- js forEach参数详解,forEach与for循环区别,forEach中如何删除数组元素
- git如何查找已经被删除文件的历史修改记录?
- 如何使用curl进行网页授权
- 人是如何变强的!
- 黑客与渗透师的区别,如何才能成为一名黑客
- 给还在迷茫的你分享我从零基础的日语文科生半路出家搞Python如何上岸的
- 如何在微信小程序中使用阿里字体图标
- 【转载】 Tensorflow如何直接使用预训练模型(vgg16为例)
- Android编程 EditView 中如何设置最多可以输入的字符数量 属性 android:ems 与 android:maxLength 的区别
- 看工程造价行业是如何玩转大数据的!
- 浅析SpringDataRedis、RedisTemplate Api的使用及如何使用RedisTemplate操作Redis的5种数据类型、StringRedisTemplate和RedisTemplate的区别
- 浏览器工作原理:浅析浏览器中的页面 - 页面性能 - 如何系统的优化页面
- 浅析ECharts使用Canvas或SVG渲染区别及如何选用、使用记录:图表背景透明、x轴y轴刻度线及文本、折线点效果、如何设置折线下面区域渐变色、坐标轴线刻度隐藏及文字设置、配置hover时突出显示图例隐藏其他图例
- windows7如何用键盘模拟鼠标操作
- 专家答疑:重复数据删除在云中是如何工作的?
- iOS - 如何得到UIImage的大小
- CAD软件中如何进行平面设备布置?
- 如何使用交换机、路由器及防火墙进行组网以及他们之间的功能和区别