手写Promise.all

122 阅读2分钟

手写Promise.all

原生Promise方法概述

Promise原生的all方法的参数接收一个数组,该数组包含若干promise对象。当所有的promise都resolve时,all方法才会resolve;但凡有一个失败了,all方法就会reject。

手写Promise.all的思路

  1. 首先我们得先判断传入的参数是否是可迭代对象,如数组、Set集合(本次示例均使用数组展示);如果不是就return;

  2. 如果传入的是一个空数组,也return;

  3. 其次需要知道传入的数组中是否都是promise对象,如果不是就将其转换为promise对象;

  4. 那么经过层层筛选,数组中各项已经全部成为Promise对象了;使用变量settledCount记录已经进入已决阶段的Promise(不管Promise是被resolve还是被reject),另外使用变量results记录进入Promise;

  5. 使用for...in循环对每一个promise进行加工,每循环到一个promise就可以调用then方法将该promise设置成resolve或者reject状态,每一个promise都会进入finally并且让settledCount++,判断settledCount === results.length,如果相等就可以返回resolve了 注意:

  6. 一定要使用for..of..循环或者for..in..循环,不要使用for循环,因为传入的可迭代对象不一定是数组,for循环中需要用到元素下标,像set集合、map集合等都没有元素下标,因此for循环不适用

  7. 如果传入的可迭代对象不是promise就要将其转换成promise

  8. 不要使用数组的push方法,因为每一个promise成为已决状态的时间点不确定,因此使用for..in..或者for..of..循环可以让每个promise成为已决后回到自身应处的位置

Promise.myAll = function (iterable) {
  //判断iterable迭代对象是否除了Promise以外还有其他参数(例如数字、字符串等)不是Promise对象,就需要使用Promise.resolve(xxx)变成Promise对象
  //使用es6的扩展运算符可以判断iterable是否是可迭代对象
  const promiseArr = [...iterable].map((item) =>
    item instanceof Promise ? item : Promise.resolve(item)
  );
  //如果promiseArr数组长度为零就resolve()掉
  if (promiseArr.length === 0) return Promise.resolve([]);

  //该方法会返回一个Promise对象
  return new Promise((resolve, reject) => {
    //用于接收resolve的promise
    let results = [];
    //用于接收已经处于已决阶段的promise
    let settledCount = 0;
    for (let p in promiseArr) {
      promiseArr[p]
        .then((res) => {
          //该项promise就设置成resolve状态
          results[p] = res;
        }, reject)
        .finally(() => {
          //不管promise被resolve了还是被reject了,settledCount都会++
          settledCount++;
          //直至已经进入已决阶段的promise的数量等于了处于resolve状态的promise就resolve
          if (settledCount === results.length) {
            resolve(results);
          }
        });
    }
  });
};