zoukankan      html  css  js  c++  java
  • 理解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,我们依旧能拿到完整的响应结果。那么到这里本文结束。

     参考

    Promise.all 处理error

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

  • 相关阅读:
    在eclipse中使用maven构建spring cloud微服务
    SpringBoot中VO,DTO,DO,PO的概念、区别和用处
    报错Connection refused: connect
    @RequestBody的使用
    Chrome插件Postman的数据目录存储位置,记一次重装系统后找回postman数据的过程...
    SpringBoot中VO,DTO,DO,PO的概念、区别和用处
    Oracle中的instr()函数 详解及应用
    for循环里的break,continue和return有什么差别
    BigDecimal转String
    字符串转为日期,日期转为字符串
  • 原文地址:https://www.cnblogs.com/echolun/p/11684886.html
Copyright © 2011-2022 走看看