手撕 promise 方法

9 阅读3分钟

手写 promise 方法

Promise.all 原生用法

Promise.all() 方法接收一个 promise 数组作为输入,并返回一个 Promise 实例。当所有 Promise 都成功时,返回的 Promise 状态变为 fulfilled,并且结果是一个包含所有 Promise 结果的数组;如果有任意一个 Promise 失败,则返回的 Promise 状态立即变为 rejected,并且结果是第一个被拒绝的 Promise 的理由。

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, "foo");
});

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values); // [3, 42, "foo"]
  })
  .catch((error) => {
    console.error(error);
  });

promiseAll

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError(`promises must be an array`));
    }

    let resolvedCounter = 0;
    let promiseNum = promises.length;
    let resolvedResult = [];

    // 如果传入空数组,直接返回空数组结果
    if (promiseNum === 0) {
      return resolve(resolvedResult);
    }

    promises.forEach((promise, index) => {
      Promise.resolve(promise).then(
        (value) => {
          resolvedCounter++;
          resolvedResult[index] = value;
          if (resolvedCounter === promiseNum) {
            resolve(resolvedResult);
          }
        },
        (error) => {
          reject(error);
        }
      );
    });
  });
}

Promise.any 原生用法

Promise.any() 方法接收一个 promise 数组作为输入,并返回一个 Promise 实例。当其中任何一个 Promise 成功时,返回的 Promise 状态变为 fulfilled,并且结果是第一个成功的 Promise 的值;如果所有 Promise 都失败,则返回的 Promise 状态变为 rejected,并返回一个包含所有拒绝原因的 AggregateError。

const promise1 = Promise.reject("error1");
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "quick"));
const promise3 = new Promise((resolve) => setTimeout(resolve, 500, "slow"));

Promise.any([promise1, promise2, promise3])
  .then((value) => {
    console.log(value); // 'quick',因为 promise2 最快完成
  })
  .catch((error) => {
    console.error(error); // 当所有 promise 都被拒绝时,这里会收到 AggregateError
  });

promiseAny

function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError("promises must be an array"));
    }

    const length = promises.length;
    if (length === 0) {
      return reject(new AggregateError([], "All promises were rejected"));
    }

    let errors = [];
    let rejectedCount = 0;

    promises.forEach((promise, index) => {
      Promise.resolve(promise).then(
        (value) => {
          resolve(value);
        },
        (error) => {
          errors[index] = error;
          rejectedCount++;
          if (rejectedCount === length) {
            reject(new AggregateError(errors, "All promises were rejected"));
          }
        }
      );
    });
  });
}

Promise.race 原生用法

Promise.race() 方法接收一个 promise 数组作为输入,并返回一个 Promise 实例。方法返回一个 Promise,一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。也就是说,返回的 Promise 的状态由第一个完成(无论是成功还是失败)的 Promise 决定。

const promise1 = new Promise((resolve) => setTimeout(resolve, 500, "one"));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, "two"));
const promise3 = new Promise((resolve, reject) =>
  setTimeout(reject, 200, "three")
);

Promise.race([promise1, promise2, promise3])
  .then((value) => {
    console.log(value); // "two",因为 promise2 最快完成
  })
  .catch((error) => {
    console.error(error);
  });

promiseRace

function promiseRace(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError("promises must be an array"));
    }

    // 空数组会一直pending
    promises.forEach((promise) => {
      Promise.resolve(promise).then(resolve, reject);
    });
  });
}

Promise.allSettled 原生用法

Promise.allSettled() 方法接收一个 promise 数组作为输入,并返回一个 Promise 实例。该方法会等待所有 Promise 完成(无论是成功还是失败),然后返回一个包含所有 Promise 结果的数组。数组中的每个元素都是一个对象,包含 status 属性(值为 "fulfilled" 或 "rejected")以及 value(fulfilled 状态下的值)或 reason(rejected 状态下的拒绝原因)属性。

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 100, "foo")
);
const promise3 = new Promise((resolve) => setTimeout(resolve, 200, 42));

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
  console.log(results);
  // [
  //   { status: 'fulfilled', value: 3 },
  //   { status: 'rejected', reason: 'foo' },
  //   { status: 'fulfilled', value: 42 }
  // ]
});

promiseAllSettled

function promiseAllSettled(promises) {
  return new Promise((resolve) => {
    if (!promises || promises.length === 0) {
      return resolve([]);
    }
    const result = [];
    let count = 0;
    const promiseLength = promises.length;

    const processResult = (value, index, status) => {
      if (status === "fulfilled") {
        result[index] = { status, value };
      } else {
        result[index] = { status, reason: value };
      }

      count++;

      if (count === promiseLength) {
        resolve(result);
      }
    };

    promises.forEach((p, index) => {
      Promise.resolve(p).then(
        (value) => {
          processResult(value, index, "fulfilled");
        },
        (reason) => {
          processResult(reason, index, "rejected");
        }
      );
    });
  });
}