likes
comments
collection

Promise 的实例方法和静态方法有哪些,如何手动实现?

作者站长头像
站长
· 阅读数 14

导读: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),会比较复杂,其余的都是基于已有功能的拓展。下面都给大伙列出所有的方法。

  1. Promise.prototype.then()
  2. Promise.prototype.catch()
  3. Promise.prototype.finally()
  4. Promise.all()
  5. Promise.race)
  6. Promise.allSettled)
  7. Promise.any()
  8. Promise.resolve()
  9. Promise.reject()

二、Promise 九种方法实现

1. 原型方法 then

then 方法是整个 Promise 解决的核心内容,同时因为回调函数和返回一个新的 Promise 实例,因此决议过程比较复杂。这里也是直接说明 then 方法的定义,具体可以看一篇文章手写一个 Promises/A+和理解核心概念

// 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);
  });
};