写点什么

【Promise 源码学习】第十一篇 - Promise.all 的实现

作者:Brave
  • 2021 年 12 月 04 日
  • 本文字数:2684 字

    阅读完需:约 9 分钟

一,前言


上一篇,主要实现了 Promise 两个实例 API(原型方法):Promise.prototype.catchPromise.prototype.finally,主要涉及以下几个点:


  • Promise.prototype.catch 功能测试、原理分析、源码实现;

  • Promise.prototype.finally 功能测试、原理分析、源码实现;


本篇,继续实现 Promise 的核心静态 API(类方法):Promise.all;

备注:Promise.all 是 Promise 中最复杂的方法;也是日常开发中的高频 API 和 面试考察点;


二,Promise.all 简介

1,API 介绍


MDN 参考资料


Promise.all功能:

  • 批量执行 Promise,返回一个 promise 实例;

  • 全部成功才算成功,返回全部执行结果;

  • 有一个失败就算失败,返回第一个失败结果;

2,原生 Promise.all 功能测试


  1. 成功示例:

Promise.all([new Promise((resolve, reject) => {  setTimeout(() => {    resolve(200)  }, 1000);}), 1]).then(data => {  console.log("then", data)}).catch(err => {  console.log('catch', err)})
// 执行结果:then [ 200, 1 ]
复制代码


备注:

  • 数字 1 不是 promise,直接被放入结果数组中;

  • 执行结果在结果数组中的顺序与执行顺序一致;


  1. 失败示例:

Promise.all([new Promise((resolve, reject) => {  setTimeout(() => {    // 1 秒后成功    resolve(200)  }, 1000);}), new Promise((resolve, reject) => {  setTimeout(() => {    // 3 秒后失败    reject(300)  }, 3000);}), new Promise((resolve, reject) => {  setTimeout(() => {    // 2 秒后失败    reject(400)  }, 2000);}), 1]).then(data => {  console.log("then", data)}).catch(err => {  console.log('catch', err)})
// 执行结果:catch 400
复制代码


备注:

  • 第一个 promise 会先执行完,但由于第三个 promise 先失败了,所以 Promise.all 会进入 catch 并返回第一个失败的结果;


三,Promise.all 实现

1,原理分析


  • Promise.all:入参是一个 promise 集合;返回一个 Promise 实例;

  • 所有 promise 的 resolve 回调结果都会被放入到一个数组;

  • 所有 promise 都执行成功且调用 resolve 后,返回的 Promise 才调用 resolve 成功,并返回全部执行结果;

  • 任何一个 promise 执行调用 reject 或抛出错误,返回的 Promise 就会调用 reject 失败,并返回第一个失败结果;

2,代码实现


Promise 中创建静态方法 all:

// Promise.all 处理 promise 集合static all (promises) {  // Promise.all 返回一个 Promise  return new Promise((resolve, reject)=>{    // 创建集合 result,用于顺序存放 Promise 执行结果    // 全部执行成功,调用 resolve(result),返回全部执行结果    let result = [];    // 遍历执行 每一个 promises    for(let i = 0;i < promises.length; i++){      let p = promises[i];      // promise 类型,promise.then      if(p && typeof p.then ==='function'){        p.then((data)=>{          // todo 执行结果放入 result        }, reject) // 任何一个 promise 失败,直接失败      // 非 promise 类型      }else{        // todo      }    }  })}
复制代码


还有以下关键问题需要解决:


问题 1:如何按照执行顺序存放异步操作的返回结果?

  • 按照异步操作执行顺序,放入 result 数字下标的对应位置;


问题 2:如何判定集合内的 promise 已经全部执行完成?

  • 如果使用 result.length,后面异步操作如果先返回放入数组,判断 length 就不准了;

  • 可以使用计数器,每次 promise 成功 index++,直至index === promises.length;


最终实现如下:

  static all (promises) {    return new Promise((resolve, reject)=>{      let result = [];      let times = 0;            // 将成功结果放入数组中对应的位置      const processSuccess = (index, val)=>{        result[index] = val;        if(++times === promises.length){            resolve(result); // 全部执行成功,返回 result        }      }        // 遍历处理集合中的每一个 promise      for(let i = 0;i < promises.length; i++){        let p = promises[i];        if(p && typeof p.then ==='function'){          // 调用这个p的 then 方法          p.then((data)=>{            // 按照执行顺序存放执行结果            processSuccess(i, data)          }, reject);        }else{          // 普通值,直接按照执行顺序放入数组对应位置          processSuccess(i, p)        }      }    })  }
复制代码

3,功能测试

// 使用自己实现的 Promise.allPromise.all([new Promise((resolve, reject) => {  setTimeout(() => {    console.log("index = 1; timeout = 1000; resolve(200)")    resolve(200)  }, 1000);}), new Promise((resolve, reject) => {  setTimeout(() => {    console.log("index = 2; timeout = 3000; reject(300)")    reject(300)  }, 3000);}), new Promise((resolve, reject) => {  setTimeout(() => {    console.log("index = 3; timeout = 2000; reject(400)")    reject(400)  }, 2000);}), 1]).then(data => {  console.log("then", data)}).catch(err => {  console.log('catch', err)})
// 输出结果// index = 1; timeout = 1000; resolve(200)// index = 3; timeout = 2000; reject(400)// catch 400// index = 2; timeout = 3000; reject(300)
复制代码


执行情况分析:

  • Promise.all 传入待执行的 promise 集合,内部遍历执行 then 方法,普通值 1 被直接放入 result[3] = 1;(注意:如果用 result.length 判定全部完成,此时的 length 已经为 4,不准)

  • 1 秒后,第一个 promise 执行成功,result[0] = 200;

  • 2 秒后,第三个 promise 执行失败,执行 reject(300) 进入 then 失败处理,就会调用 Promise.all 内部 new Promise 的 reject,导致 Promise.all 返回一个失败的 promise 对象结果为 300;(注意:此时第二个 promise 已经执行但未执行完成,Promise.all 的失败不会影响它的继续执行,也无法让他停止);

  • 3 秒后,第二个 promise 执行失败,执行 reject(400),但由于 Promise.all 已经失败,所以内部创建的 new Promise 实例已经是失败态,不能继续被改变状态;


自己实现的 Promise.all 与原生 Promise.all 表现一致;


四,结尾


本篇,主要实现 Promise 的核心静态 API(类方法):Promise.all,主要涉及以下几个点:


  • 测试原生 Promise.all 的使用;

  • Promise.all 的功能与特性分析;

  • Promise.all 的源码实现、执行分析、功能测试;


下一篇,继续 Promise 静态 API:Promise.race;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【Promise 源码学习】第十一篇 - Promise.all 的实现