理解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实例,那么在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 处理error

理解和使用Promise.all和Promise.race

原文地址:https://www.cnblogs.com/echolun/p/11684886.html

时间: 2024-10-11 16:44:20

理解Promise.all,Promise.all与Promise.race的区别,如何让Promise.all在rejected后依然返回resolved状态的相关文章

理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用

原文:理解C# 4 dynamic(1) - var, object, dynamic的区别以及dynamic的使用 阅读目录: 一. 为什么是它们三个 二. 能够任意赋值的原因 三. dynamic的用法 四. 使用dynamic的注意事项 一. 为什么是它们三个? 拿这三者比较的原因是它们在使用的时候非常相似.你可以用它们声明的变量赋任何类型的值. 看看下面的示例: var a = 1; object b = 1; dynamic c = 1; 你还可以使用关键字为它们赋上更加复杂的类型 v

连续张量理解和contiguous()方法使用,view和reshape的区别

连续张量理解和contiguous()方法使用,view和reshape的区别 待办 内存共享: 下边的x内存布局是从0开始的,y内存布局,不是从0开始的张量 For example: when you call transpose(), PyTorch doesn't generate new tensor with new layout, it just modifies meta information in Tensor object so offset and stride are f

老张喝茶 教你同步异步 阻塞与非阻塞(理解同步异步,阻塞与非阻塞的区别)

最近看网络编程方面的书,看到后面突然发现,同步异步,阻塞非阻塞似乎是同一个概念,其实不然 老张爱喝茶,废话不说,煮开水. 出场人物:老张,水壶两把(普通水壶,简称水壶:会响的水壶,简称响水壶). 1 老张把水壶放到火上,立等水开.(同步阻塞) 老张觉得自己有点傻 2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有.(同步非阻塞) 老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶.水开之后,能大声发出嘀~~~~的噪音. 3 老张把响水壶放到火上,立等水开.(异步阻塞) 老张

简单理解动态内存分配和静态内存分配的区别

在涉及到内存分配时,我们一般都要考虑到两种内存分配方式,一种是动态内存分配,另一种是静态内存分配,我们该怎么理解这两者的区别呢? 在我看来,静态内存分配和动态内存分配比较典型的例子就是数组和链表,数组的长度是预先定义好的,在整个程序中是固定不变的,所以他在内存分配时是以静态内存分配的方式进行的.而链表,它的信息有可能会随时更改,内存的分配取决于我们实际输入的数据,这样就用到了动态内存分配的方式. 静态内存分配是在程序编译或者运行过程中,按事先规定的大小分配内存空间的分配方式,他的前提的必须事先知

深入理解C# 静态类与非静态类、静态成员的区别

静态类 静态类与非静态类的重要区别在于静态类不能实例化,也就是说,不能使用 new 关键字创建静态类类型的变量.在声明一个类时使用static关键字,具有两个方面的意义:首先,它防止程序员写代码来实例化该静态类:其次,它防止在类的内部声明任何实例字段或方法. 静态类是自C# 2.0才引入的,C# 1.0不支持静态类声明.程序员必须声明一个私有构造器.私有构造器禁止开发者在类的范围之外实例化类的实例.使用私有构造器的效果与使用静态类的效果非常相似. 两者的区别:私有构造器方式仍然可以从类的内部对类

怎样理解封装,继承,多态!三者的区别?

1.类是对对象的一个抽象,同时类也为对象进行了封装.所谓封装是说类的设计者只是为使用者提供类 对象可以访问的部分,而对于类中其他隐藏起来的成员变量 方法,用户不能访问. 实现方式:A:在类的定义中设置对对象中成员变量和方法进行访问的权限; B:提供一个统一的供其他类引用的方法; C:其它对象不能直接修改文本对象所拥有的属性和方法. 2.访问权限: A:private的成员变量和方法只能被这个类本身的方法访问; B:默认的成员变量和方法只被同一个包内的其它所有类都可访问,包外不可; C: prot

java入门概念个人理解之从字符串比较到==和equals方法区别

我们先看代码 String str1 = new String("hello"); String str2 = "hello"; System.out.println("str1==str2: " + (str1==str2)); \\1 System.out.println("str1.equals(str2): " + str1.equals(str2)); \\2 输出结果: str1==str2: false str1

深入理解《字符指针与字符数组真正的区别》

来自: http://blog.csdn.net/on_1y/article/details/13030439 (这篇介绍的非常到位和透彻!!!) char *p="hello"; char q[]="hello"; char *r = (char*)malloc(sizeof(char)*6); 我们知道,字符指针和字符数组,都可以用来存储和表达字符串. 但,它们的实现方式是不同的! 下面,从代码初始化的角度,来分别说明. (1)  char *p="h

深入理解使用synchronized同步方法和同步代码块的区别

一.代码块和方法之间的区别 首先需要知道代码块和方法有什么区别: 构造器和方法块,构造器可以重载也就是说明在创建对象时可以按照不同的构造器来创建,那么构造器是属于对象,而代码块呢他是给所有的对象初始化的.底下看一个列子: public class Constructor_Methodblock { private int num; private String str; //构造器 public Constructor_Methodblock(int num,String str){ Syste