Promise静态方法实现--JS手写代码题(二)

123 阅读3分钟

Promise 类提供了四个静态方法来促进异步任务的并发.

Promise.all

接受一个 Promise 可迭代对象作为输入,并返回单个 Promise。返回的 Promise 在所有输入的 Promise 都兑现时(包括传入的可迭代对象为空时)被兑现,其值为一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,返回的 Promise 也会被拒绝,并返回第一个拒绝的原因。

/**
 * 实现Promise.all
 */
function promiseAll(promiseList) {
  // 校验参数
  if (!Array.isArray(promiseList)) {
    throw new Error("promiseAll argument must be an array");
  }
  // promise为0的时候之间返回
  if (promiseList.length === 0) {
    return Promise.resolve([]);
  }

  let count = 0;
  const len = promiseList.length;
  const result = new Array(len);
  return new Promise((resolve, reject) => {
    promiseList.forEach((p, index) => {
      p.then((res) => {
        result[index] = res;
        count++;
        if (count === len) {
          resolve(result);
        }
      }).catch((e) => {
        reject(e);
      });
    });
  });
}

Promise.allSettled

接受一个 Promise 可迭代对象作为输入,并返回单个 Promise。返回的 Promise 在所有输入的 Promise 都敲定时兑现(包括传入的可迭代对象为空时),其值为一个描述每个 Promise 结果的对象数组。

方法返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象

function promiseAllSetted(promiseList) {
  // 校验参数
  if (!Array.isArray(promiseList)) {
    throw new Error("promiseAll argument must be an array");
  }
  // promise为0的时候之间返回
  if (promiseList.length === 0) {
    return Promise.resolve([]);
  }

  let count = 0;
  const len = promiseList.length;
  const result = new Array(len);
  return new Promise((resolve) => {
    promiseList.forEach((p, index) => {
      p.then((res) => {
        result[index] = {
          value: res,
          status: "fulfilled",
        };
      })
        .catch((err) => {
          result[index] = {
            reason: err,
            status: "rejected",
          };
        })
        .finally(() => {
          count++;
          if (count === len) {
            resolve(result);
          }
        });
    });
  });
}

Promise.any

接受一个 Promise 可迭代对象作为输入,并返回单个 Promise。返回的 Promise 在任何输入的 Promise 兑现时兑现,其值为第一个兑现的值。如果所有输入的 Promise 都被拒绝(包括传入的可迭代对象为空时),返回的 Promise 将以带有一个包含拒绝原因的数组的 AggregateError 拒绝。

Promise.any()主要是针对只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

function promiseAny(promiseList) {
  // 校验参数
  if (!Array.isArray(promiseList)) {
    throw new Error("promiseAll argument must be an array");
  }
  // promise为0的时候之间返回
  if (promiseList.length === 0) {
    return Promise.reject(new AggregateError([]));
  }

  let count = 0;
  const errors = [];
  const len = promiseList.length;
  return new Promise((resolve, reject) => {
    promiseList.forEach((p) => {
      p.then(resolve).catch((err) => {
        count++;
        errors.push(err);
        if (count === len) {
          reject(new AggregateError(errors));
        }
      });
    });
  });
}

Promise.race

接受一个 Promise 可迭代对象作为输入,并返回单个 Promise。返回的 Promise 与第一个敲定的 Promise 的最终状态保持一致。

race是竞赛赛跑的意思,竞赛肯定最受关注的就是第一名,其他的就无所谓了。

function promiseRace(promiseList) {
  // 校验参数
  if (!Array.isArray(promiseList)) {
    throw new Error("promiseAll argument must be an array");
  }

  // 如果传入的是空数组,返回的就是pending状态的Promise
  return new Promise((resolve, reject) => {
    promiseList.forEach((p) => p.then(resolve, reject));
  });
}

如何利用AbortController实现一个可取消的Promise

我们知道Promise本身是无法取消的,但是我们可以利用AbortController自己实现一个带有取消功能的Promise。

AbortController是js本身提供的Api,比较重要的是它的属性signal和方法abort。fetch方法也支持传入一个signal进行取消。具体可以看看这里:developer.mozilla.org/zh-CN/docs/…

class CancelablePromise<T> {
  
  /**
   * 构造器
   * @param executor Promise中的 executor
   * @param abortSignal AbortController中的signal对象
   * @returns 
   */
  constructor(
    executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void, 
    abortSignal: AbortSignal
  ) {
    
    // 记录reject和resolve方法
    let _reject: any = null;
    let _resolve: any = null;
    // 标志是否已经解决了
    let _isExecResolve = false;
    
    // 创建和执行Promise
    const cancelablePromise = new Promise<T>((resolve, reject) => {
      _reject = reject;
      _resolve = (value: T) => {
        _isExecResolve = true;
        resolve(value);
      };
      return executor(_resolve, reject);
    });
    
    // 监听Signal的abourt事件
    abortSignal.addEventListener('abort', () => {
      if (_isExecResolve) {
        return;
      }
      // 抛出错误
      const error = new DOMException('user cancel promise', CancelablePromise.CancelExceptionName );
      _reject( error );
    } );
    return cancelablePromise;
  }
  
  // 取消后抛出的异常名称
  static CancelExceptionName = 'CancelablePromise AbortError';
}

export default CancelablePromise;

项目源码仓库: github.com/ryan6015/fr…

上一篇:前端面试--JS手写代码题(一)

下一篇:JS中字符串/数组原生方法--JS手写代码题(三)