Promise.all()使用方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Promise.all()使用方法相关的知识,希望对你有一定的参考价值。

参考技术A 在实际项目中,可能会遇到 需要从前两个接口中的返回结果获取第三个接口的请求参数这种情况。 也就是需要等待两个/多个异步事件完成后,再进行回调。
对于异步回调,首先想到的就会是使用Promise封装,然后使用.then()来触发回调。那么对于两个或多个异步事件均完成后再触发回调可以使用Promise.all()方法。

**Promise.all(iterable)** 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

摘自 官方说明

理解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()使用方法的主要内容,如果未能解决你的问题,请参考以下文章

await 与 Promise.all 结合使用

await 与 Promise.all 结合使用

vue promise.all使用

使用 Promise.all() 在 Promise 实现时执行操作

Promise.all和Promise.race区别,和使用场景

手动实现Promise.all()