Promise.all 方法详解

 前言:

本文对 promise.all 方法进行讲解 ,参考 阮一峰老师的ES6

需要了解 promise 基础的小伙伴 可以看  我的另一篇文章


 Promise 对象实现 Ajax

通过 promise 对象实现 Ajax (如下) ,后面 promise.all 会使用到  promise

下面代码中, getJSON 是对 XMLHttpRequest 对象的封装,用于发出一个获取数据的 HTTP 请求,并且返回一个 Promise 对象。需要注意的是,在 getJSON 内部,resolve 函数和 reject 函数调用时,都带有参数。

 获取数据接口 : https://jsonplaceholder.typicode.com/users

 const getJSON = function(url) {
    const promise = new Promise(function(resolve, reject){
      const handler = function() {
        if (this.readyState !== 4) {
          return;
        }
        if (this.status === 200) {
          resolve(this.response);
        } else {
          reject(new Error(this.statusText));
        }
      };
      const client = new XMLHttpRequest();
      client.open("GET", url);
      client.onreadystatechange = handler;
      client.responseType = "text";
      client.setRequestHeader("Accept", "application/json");
      client.send();
  
    });
  
    return promise;
  };
  
  getJSON("https://jsonplaceholder.typicode.com/users").then(function(json) {
    console.log('Contents: ' + json);
  }, function(error) {
    console.error('出错了', error);
  })

 请求结果

 这个数据接口 共有十条数据 ,全部请求成功

Promise.all

Promise .all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

const p = Promise.all([p1, p2, p3]);

 上面代码中,Promise .all()方法接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果不是,就会先调用 Promise .resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise .all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

P 的状态由 p1、p2、p3 决定,分成两种情况。

(1)只有 p1、p2、p3 的状态都变成 fulfilled, 的状态才会变成 fulfilled

此时p1、p2、p3 的返回值组成一个数组,传递给  p的回调函数。

(2)只要 p1、p2、p3 之中有一个被 rejected的状态就变成 rejected

此时第一个被 reject  的实例的返回值,会传递给 的回调函数。

使用上面的  getJSON 完成 promise.all 的例子

  // 生成一个Promise对象的数组
    const promises = [1, 3, 5, 7, 9].map(function (id) {
    return getJSON('https://jsonplaceholder.typicode.com/users/' + id);
    });

    Promise.all(promises).then(function (posts) {
        console.log("ok")
        console.log('Contents: ' + posts);
    }).catch(function(reason){
        console.log('出错了',reason)
    });

 上面代码中,promises 是包含 5个 Promise 实例的数组,只有这 5 个实例的状态都变成fulfilled,或者其中有一个变为 rejected,才会调用 Promise.all 方法后面的回调函数。

 请求成功例子

上面的代码正常运行 ,打印 ok 以及  id 为 1  3  5  7  9  的数据


请求失败例子

上面 5 个 Promise 实例的数组 只要有一个变为  rejected,就会调用 Promise .all()的catch

 接口 只有10个数据 ,我把一个 ID 改成 19 ,这个地址不存在自然就报错 ,调用 Promise .all() catch


 另外一个例子

const databasePromise = connectDatabase();

const booksPromise = databasePromise
  .then(findAllBooks);

const userPromise = databasePromise
  .then(getCurrentUser);

Promise.all([
  booksPromise,
  userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));

上面代码中,booksPromise和 userPromise 是两个异步操作,只有等到它们的结果都返回了,

才会触发 pickTopRecommendations这个回调函数。

 注意:

如果作为参数的 Promise 实例,自己定义了 catch 方法,那么它一旦被 rejected,并不会触发 Promise .all() catch 方法。

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

上面代码中,p1 会 resolvedp2 首先会 rejected,但是 p2 有自己的 catch 方法,该方法返回的是一个新的 Promise 实例, p2 指向的实际上是这个实例。该实例执行完 catch 方法后,也会变成 resolved,导致 Promise .all()方法参数里面的两个实例都会 resolved,因此会调用 then 方法指定的回调函数,而不会调用 catch 方法指定的回调函数。

如果 p2 没有自己的 catch 方法,就会调用 Promise .all()的 catch 方法。(如下)

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// Error: 报错了

 手写 promise.all 

接受的参数 实际上是迭代器 Iterable, 这里使用 数组代替 ,数组是特殊的迭代器

迭代器转数组 使用  Array.from()

function pAll (_promises) {
    return new Promise((resolve, reject) => {
      // Iterable => Array
      const promises = Array.from(_promises)
      // 结果用一个数组维护
      const r = []
      const len = promises.length
      let count = 0
      for (let i = 0; i < len; i++) {
        // Promise.resolve 确保把所有数据都转化为 Promise
        Promise.resolve(promises[i]).then(o => { 
          // 因为 promise 是异步的,保持数组一一对应
          r[i] = o;
  
          // 如果数组中所有 promise 都完成,则返回结果数组
          if (++count === len) {
            resolve(r)
          }
          // 当发生异常时,直接 reject
        }).catch(e => reject(e))
      }
    })
 }

// 为了测试,实现一个 sleep 函数 
const sleep = (seconds) => 
new Promise(resolve => setTimeout(() => resolve(seconds), seconds))

const p1 = sleep(1000);
const p2 = sleep(2000);
const p3 = Promise.reject(10000)
pll([p1, p2, p3]).then(o => console.log(o)).catch(e => console.log( ' Error->', e))

 Promise.all 中的任何一个 promise 执行失败(即返回 rejected 状态),则 Promise.all 将立即返回一个 rejected 状态的 promise,并且 不会等待其他未完成的 promise。只有在所有 promise 都成功执行时,Promise.all 才会返回 resolved 状态的 promise,并将所有 promise 的结果收集到一个数组中。


 输出结果 


文章如有错误,恳请大家提出问题,本人不胜感激 。 不懂的地方可以评论,我都会一一回复

文章对大家有帮助的话,希望大家能动手点赞鼓励,大家未来一起努力     长路漫漫,道阻且长  

  • 51
    点赞
  • 107
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潮汐未见潮落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值