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

Posted echolun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解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

以上是关于理解Promise.all,Promise.all与Promise.race的区别,如何让Promise.all在rejected后依然返回resolved状态的主要内容,如果未能解决你的问题,请参考以下文章

关于 Pomise.all的理解与使用

promise.all 怎么用

实现promise.all方法

在嵌套 forEach 中的 Promise.all 之前评估 Promise,导致 Promise.all 为空

Promise.all()使用方法

深入理解Promise并写一个符合Promise a+规范的Promise代码