如何实现Promise 的实例方法和静态方法

Promise 是异步编程的一种解决方案,比传统的回调函数或事件更合理和更灵活。本文主要展示 Promise 提供的方法列表,以及基本的实现原理。通过本文,我们能加深 Promise 方法的理解和场景使用,对 Promise.all、Promise.race、Promise.allSettled 和 Promise.any 四个方法的异同之处也有更深层次的领悟。

一、Promise 方法列表
Promise 的实例方法有 then/catch/finally 三种,静态方法有 all/race/allSettled/any/resolve/reject 六种。其中 then 实现起来因为涉及 Promise 裁决过程(The Promise Resolution Procedure),会比较复杂,其余的都是基于已有功能的拓展。下面都给大伙列出所有的方法。

Promise.prototype.then()
Promise.prototype.catch()
Promise.prototype.finally()
Promise.all()
Promise.race)
Promise.allSettled)
Promise.any()
Promise.resolve()
Promise.reject()
二、Promise 九种方法实现
1. 原型方法 then
then 方法是整个 Promise 解决的核心内容,同时因为回调函数和返回一个新的 Promise 实例,因此决议过程比较复杂。

// class MyPromise {
//     static PENDING = 'pending'; // 进行中
//     static FULFILLED = 'fulfilled'; // 已成功
//     static REJECTED = 'rejected'; // 已失败
//     state = MyPromise.PENDING;
//     value = null;
//     reason = null;
//     onFulfilledCallbacks = [];
//     onRejectedCallbacks = [];
//     ...
// }
MyPromise.prototype.then = (onFulfilled, onRejected) => {
  if (typeof onFulfilled != 'function') {
    onFulfilled = (value) => value;
  }
  if (typeof onRejected != 'function') {
    onRejected = (reason) => {
      throw reason;
    };
  }
  // Promise 核心解决过程 见规范2.3
  const _resolvePromise = (promise, x, resolve, reject) => {
    // 2.3.1 如果 promise 和 x 指向同一对象,抛出 TypeError 错误
    if (promise === x) {
      const errMsg = 'The promise and the return value are the same';
      return reject(new TypeError(errMsg));
    }

    // 2.3.3 如果 x 为对象(不是null)或者函数
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
      let then = null;
      try {
        // 2.3.3.1. 检索属性 x.then
        then = x.then;
      } catch (error) {
        // 2.3.3.2 如果 x.then 导致抛出异常 e,则以 e 为拒绝原因拒绝 promis
        return reject(error);
      }

      // 2.3.3.3 如果 then 是一个函数,x 作为 then 的 this 调用该方法,第一个参数是成功的回调函数,第二个参数是失败的回调函数
      if (typeof then === 'function') {
        let called = false;
        try {
          then.call(
            x,
            (y) => {
              // 2.3.3.3.4 如果成功回调与失败回调都被调用或多次调用同一个参数,则第一个调用优先,其他调用都将被忽略。
              if (called) return;
              called = true;
              // 2.3.3.3.1 如果成功回调以值 y 调用,运行 [[Resolve]](promise,y)
              _resolvePromise(promise, y, resolve, reject);
            },
            (r) => {
              // 2.3.3.3.4 如果成功回调与失败回调都被调用或多次调用同一个参数,则第一个调用优先,其他调用都将被忽略。
              if (called) return;
              called = true;
              // 2.3.3.3.2 如果失败回调以原因 r 调用,用 r 拒绝 promise
              reject(r);
            }
          );
        } catch (error) {
          // 2.3.3.4 如果调用 then 方法抛出异常 e:
          // 2.3.3.4.1 若成功回调或失败回调都调用过,忽略
          if (called) return;

          // 2.3.3.4.2 未调用,用 e 作为原因拒绝 promise
          reject(error);
        }
      } else {
        // 2.3.3.4. 如果 then 不是函数,用 x 作为值完成 promise
        return resolve(x);
      }
    } else {
      // 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
      return resolve(x);
    }
  };
  // 链式返回的Promise
  const newPromise = new MyPromise((resolve, reject) => {
    switch (this.state) {
      case MyPromise.FULFILLED:
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            _resolvePromise(newPromise, x, resolve, reject);
          } catch (reason) {
            reject(reason);
          }
        }, 0);
        break;
      case MyPromise.REJECTED:
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            _resolvePromise(newPromise, x, resolve, reject);
          } catch (reason) {
            reject(reason);
          }
        }, 0);
        break;
      case MyPromise.PENDING:
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              _resolvePromise(newPromise, x, resolve, reject);
            } catch (reason) {
              reject(reason);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              _resolvePromise(newPromise, x, resolve, reject);
            } catch (reason) {
              reject(reason);
            }
          }, 0);
        });
        break;
    }
  });
  return newPromise;
};
2. 原型方法 catch
如果上面没有定义 reject 方法或者在抛出错误,那么所有的异常会走向 catch 方法,而 catch 可以复用 then 方法。

MyPromise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
};
3. 原型方法 finally
不管是 resolve 还是 reject 都会调用 finally 。那么相当于 fianlly 方法替使用者分别调用了一次 then 的 resolved 和 rejected 状态回调。

MyPromise.prototype.finally = function (fn) {
  return this.then(
    (value) => {
      fn();
      return value;
    },
    (reason) => {
      fn();
      throw reason;
    }
  );
};
4. 静态方法 Promise.all
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。所有参数数组 Promise 实例执行 resolve 回调后,新实例执行 resolve 回调;如果中间有任何一个 Promise 实例执行 reject 回调,那么新实例就直接执行 reject 回调了。

打个比方:多名员工分别同时进行多个项目,你要求任何一个项目都必须是令你满意的,有一个不满意都算这件事(所有项目)是失败的。强调的是整体令你满意。

MyPromise.all = function (promises) {
  return new Promise((resolve, reject) => {
    if (promises.length === 0) {
      resolve([]);
    } else {
      let result = [];
      let index = 0;
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (data) => {
            result[i] = data;
            if (++index === promises.length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err);
            return;
          }
        );
      }
    }
  });
};
5. 静态方法 Promise.race
Promise.race() 顾名思义,就是竞赛,返回最快完成那一个 Promise 实例。只要参数数组中有一个 Promise 实例执行 resolve 回调或 reject 回调后,新实例就直接返回结果。

打个比方:多名员工分别同时进行多个项目,你只要最快完成的项目,无论项目是否令你满意。强调的是要快。

MyPromise.race = function (promises) {
  return new Promise((resolve, reject) => {
    if (promises.length === 0) {
      resolve();
    } else {
      let index = 0;
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (data) => {
            resolve(data);
          },
          (err) => {
            reject(err);
            return;
          }
        );
      }
    }
  });
};
6. 静态方法 Promise.allSettled
Promise.allSettled() 方法只有等到参数数组的所有 Promise 实例都发生状态变更,返回的 Promise 实例才会发生状态变更,无论是执行 resolve 回调还是 reject 回调的状态。

打个比方:多名员工分别同时进行多个项目,你要求每个一个项目都必须完成,然后得到所有项目是令你满意还是令你不满意的。强调的是最终结果。

MyPromise.allSettled = function (promises) {
  return new Promise((resolve, reject) => {
    if (promises.length === 0) {
      resolve([]);
    } else {
      let result = [];
      let index = 0;
      for (let i = 0; i < promises.length; i++) {
        promises[i]
          .then(
            (value) => {
              result[i] = {
                status: 'fulfilled',
                value,
              };
            },
            (reason) => {
              result[i] = {
                status: 'rejected',
                reason,
              };
            }
          )
          .finally(() => {
            if (++index === promises.length) {
              return resolve(result);
            }
          });
      }
    }
  });
};
同时因为 Promise.allSettled() 和 Promise.all() 都是对所有 Promise 实例的一种处理,下面就可以利用 Promise.all() 来实现 Promise.allSettled() 方法。

MyPromise.allSettled = function (promises) {
  return Promise.all(
    promises.map((item) =>
      Promise.resolve(item).then(
        (value) => ({ status: 'fulfilled', value }),
        (reason) => ({ status: 'rejected', reason })
      )
    )
  );
};
7. 静态方法 Promise.any
Promise.any() 方法是返回任意一个最快执行 resolve 回调的 Promise 实例。

打个比方:多名员工同时进行多个项目,你只要最快得到令你满意的那个项目,无论所有项目最终都没令你满意,这件事(所有项目)才算是失败了。强调的是又快又好。

MyPromise.any = function (promises) {
  return new Promise((resolve, reject) => {
    if (promises.length === 0) {
      return resolve();
    } else {
      let result = [];
      let index = 0;
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (value) => {
            return resolve(value);
          },
          (reason) => {
            result[i] = reason;
            if (++index === promises.length) {
              return reject(new AggregateError(result));
            }
          }
        );
      }
    }
  });
};
8. 静态方法 Promise.resolve
Promise.resolve 方法返回一个以给定值解析后的 Promise 实例。相当于执行 then 方法里面的 _resolvePromise。

MyPromise.reject = function (value) {
  return new MyPromise((resolve, reject) => {
    resolve(value);
  });
};
9. 静态方法 Promise.reject
Promise.reject 方法返回一个带有拒绝原因的 Promise 实例。

MyPromise.reject = function (reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
};

你可能感兴趣的