Promise 原理及实现

109 阅读33分钟

一、前言

Promise 是 Javascript 中实现异步编程的方式之一,本文将遵循 Promises/A+ 规范 实现 Promise 的主要功能。

主要实现以下几个部分:

  • 实例方法
    • then
    • catch
    • finally
  • 静态方法
    • resolve
    • reject
    • all
    • race
    • any
    • allSettled

二、基本实现

2.1 使用方法

先来看下 Promise 的基本用法,举一个简单例子:

const p = new Promise((resolve, reject) => {
    if (a === b) {
        resolve('yes');
    } else {
        reject('no');
    }
}).then(value => {
    console.log(value);
}, reason => {
    console.log(reason);
});

通过上面这个例子,我们来了解一下 Promise 的基本概念:

  1. Promise 实例会有三种状态:等待态(pending)、成功态(fulfilled)和失败态(rejected),初始状态为 pending,当状态发生变化时,后续状态不可变化,即只有两种变化过程:pending -> fulfilled 或者 pending -> rejected;
  2. 创建 Promise 实例时会传入一个执行器函数 executor,这个函数是立即执行的;
  3. 执行器函数会有两个参数:resolve 和 reject,当调用 resolve 方法时,Promise 会立即由 pending 变为 fulfilled,当调用 reject 方法时,Promise 会立即由 pending 变为 rejected;
  4. Promise 实例具有 then 方法,then 方法接受两个函数 onFulfilled 和 onRejected,当状态变为 fulfilled 时,会触发 onFulfilled 函数,当状态变为 rejected 时,会触发 onRejectd 函数,这两个函数分别接收 resolve 和 reject 传入的值。

2.2 定义状态

首先,定义一个自定义类 MyPromise,因为 Promise 具有三种状态,我们可以把这三种状态定义到 MyPromise 的静态变量中。

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";
}

接着,我们需要一个变量记录当前 Promise 的状态,可以在 MyPromise 中定义一个私有变量 #status,初始值为 pending。同时,还需要两个变量用于记录执行器函数中 resolve 和 reject 方法传入的参数,可以定义两个私有变量:#value#reason,初始值为 null:

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";
  
  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
}

2.3 实现执行器函数 executor

由于 executor 函数是立即执行的,因此可以方法 MyPromise 的构造函数中:

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";
  
  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  
  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }
}

在构造器函数中,我们主要实现了两个方法 resolvereject,现在这两个方法的作用主要是改变 Promise 的状态并记录下成功或者失败的值。

当同步执行 executor 函数时,可能会出现错误(比如,用户传入的 executor 不是函数,或者执行 executor 过程中引发报错)。这里,我们使用 try catch 拦截错误,并将错误交给 reject 方法执行。

2.4 实现 then 方法

当 Promise 状态发生改变时,需要执行 then 方法里的 onFulfilled 或者 onRejected 函数:

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";
  
  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  
  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }
  
  then(onFulfilled, onRejected) {
      if (this.#status === MyPromise.FULFILLED) {
          onFulfilled(this.#value);
      } else if (this.#status === MyPromise.REJECTED) {
          onRejected(this.#reason);
      }
  }
}

2.4.1 异步问题

在以上实现中,then 方法只有在执行器函数执行后,Promise 状态同步发生改变时才能生效。比如:

new MyPromise((resolve, reject) => {
    resolve(1);
}).then(value => console.log(value));

因为执行器函数执行完后,MyPromise 立即变为成功态并且记录成功值为 1,运行到 then 方法里时,由于 MyPromise 的 status 为 FULFILLED,所以会执行 onFulfilled 函数,也就是打印 value 值为 1。

那么,如果执行器函数里的 resolve 或者 reject 异步执行呢?比如:

new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    });
}).then(value => console.log(value));

由于,resolve 函数异步执行,当执行到 then 方法里时,MyPromise 的状态仍然为 PENDING,所以不会执行 onFulfilled 或者 onRejected 回调函数,并且当 resolve(1) 被异步执行完之后,MyPromise 收不到任何通知,也就无法执行 onFulfilled 函数,此时 MyPromise 的功能将会失效。

解决这个问题的关键就是,如何在 MyPromise 状态发生改变时,通知到 MyPromise,让其执行 then 方法里的回调函数?

这里,Promise 使用了发布订阅的思想来解决这个问题:

  1. 执行 then 方法时,如果此时 Promise 的状态为 PENDING,那么将 onFulfilled 和 onRejected 回调函数分别存到两个队列 resolveCallbacks 和 rejectCallbacks 中【收集订阅依赖】;
  2. 在之后的某个时刻,如果异步执行完 resolve 或者 reject 方法后,依次执行完 resolveCallbacks 或者 rejectCallbacks 队列中的所有回调函数【发布更新通知】。

那么,为什么要用队列存储多个回调函数?只存一个可以吗?

这是因为一个 Promise 实例可能调用了多次 then 方法,所以需要存储多个回调函数。之所以选择用队列存储,是因为要保证回调函数能够按照先进先出的顺序执行。

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";
  
  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  /**
   * 【私有变量】成功回调队列,存储多个 onFulfilled 回调函数
   */
  #resolveCallbacks = [];
  /**
   * 【私有变量】失败回调队列,存储多个 onRejected 回调函数
   */
  #rejectCallbacks = [];

  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
        // 当同步调用 resolve 时,#resolveCallbacks 为空
        // 主要是为了异步处理成功回调队列
        this.#resolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
        // 当同步调用 reject 时,#rejectCallbacks 为空
        // 主要是为了异步处理失败回调队列
        this.#rejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }
  
  then(onFulfilled, onRejected) {
      switch (this.#status) {
          case MyPromise.PENDING:
              this.#resolveCallbacks.push(() => onFulfilled(this.#value));
              this.#rejectCallbacks.push(() => onRejected(this.#reason));
              break;
          case MyPromise.FULFILLED:
              onFulfilled(this.#value);
              break;
          case MyPromise.REJECTED:
              onRejected(this.#reason);
              break;
      }
  }
}

现在,我们来测试一下上面的代码:

const a = new MyPromise((resolve) => {
  setTimeout(() => resolve("111"));
});
a.then((value) => {
  console.log("a", value);
});
a.then((value) => {
  console.log("b", value);
});
a.then((value) => {
  console.log("c", value);
});
setTimeout(() => {
  a.then((value) => {
    console.log("d", value);
  });
}, 2000);

先来看看执行顺序:

  1. 执行 MyPromise 的执行器函数,此时将 () => resolve("111") 回调函数交给定时器触发线程执行,此时 MyPromise 状态未改变,仍然为 PENDING;
  2. 执行 a.then(),因为 Promise 状态为 PENDING,所以将 (value) => { console.log("a", value) } 回调函数存入 resolveCallbacks 中;
  3. 执行 a.then(),同理,将 (value) => { console.log("b", value) } 存入 resolveCallbacks 中;
  4. 执行 a.then(),同理,将 (value) => { console.log("c", value) } 存入 resolveCallbacks 中;
  5. 执行 setTimeout(),将 a.then((value) => { console.log("d", value) }) 回调函数交给定时器触发线程执行;
  6. 第一个定时器计时结束,定时器触发线程返回 () => resolve("111") 回调函数给主线程执行,此时 MyPromise 状态变为 FULFILLED,记录成功值为 "111",并且顺序执行 resolveCallbacks 里的回调函数:[(value) => { console.log("a", value) }, (value) => { console.log("b", value) }, (value) => { console.log("c", value) }],所以此时会输出:
a 111
b 111
c 111
  1. 过了 2s 后,第二个定时器计时结束,定时器触发线程返回 a.then((value) => { console.log("d", value) }) 回调函数给主线程执行,由于此时 MyPromise 的状态为 FULFILLED,所以 then 方法里会直接执行 onFulfilled 函数,也就是 (value) => { console.log("d", value) },最终输出为:
a 111
b 111
c 111
d 111

注意,Promise 中 then 方法应当是微任务,此时还尚未实现该功能,所以当成同步代码执行。

2.4.2 链式调用问题

Promise 支持链式调用,即 then 方法调用后的返回值仍然能使用 then 方法:

new Promise((resolve, reject) => {
    resolve(111);
}).then(value => {
    console.log('first then: ', value);
}).then(value => {
    console.log('second then: ', value);
});

要实现链式调用的功能,最简单的方法就是返回一个新的 Promise 的对象,也就是 then 方法的返回值是一个新的 Promise 对象。如下:

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";

  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  /**
   * 【私有变量】成功回调队列,存储多个 onFulfilled 回调函数
   */
  #resolveCallbacks = [];
  /**
   * 【私有变量】失败回调队列,存储多个 onRejected 回调函数
   */
  #rejectCallbacks = [];

  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
        // 当同步调用 resolve 时,#resolveCallbacks 为空
        // 主要是为了异步处理成功回调队列
        this.#resolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
        // 当同步调用 reject 时,#rejectCallbacks 为空
        // 主要是为了异步处理失败回调队列
        this.#rejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      switch (this.#status) {
        case MyPromise.PENDING:
          this.#resolveCallbacks.push(() => {
            // 执行回调函数时可能出错,所以需要捕获错误
            try {
              onFulfilled(this.#value);
            } catch (err) {
              reject(err);
            }
          });
          this.#rejectCallbacks.push(() => {
            // 同上
            try {
              onRejected(this.#reason);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case MyPromise.FULFILLED:
          // 同上
          try {
            onFulfilled(this.#value);
          } catch (err) {
            reject(err);
          }
          break;
        case MyPromise.REJECTED:
          // 同上
          try {
            onRejected(this.#reason);
          } catch (err) {
            reject(err);
          }
          break;
      }
    });

    return promise2;
  }
}

以上的实现虽然能实现链式调用,但是链式中的第二个及后面的 then 中的回调函数却无法生效:

new MyPromise((resolve, reject) => {
  resolve(1);
})
.then((value) => {
    console.log("then1:", value);
})
.then((value) => {
    console.log("then2:", value);
})
.then((value) => {
    console.log("then3:", value);
});

// 输出:
// then1: 1

这是因为第一个 then 方法返回的 promise2 中,执行器函数中没有执行 resolve 或者 reject 方法,所以 Promise 状态一直是 PENDING,也就无法触发 then 方法里的回调函数。

那么 promise2 中何时调用 resolve 或者 reject 方法呢?以及如果要执行 resolve 或者 reject 方法,传入的值应该是什么呢?这就涉及到接下来处理 then 中回调函数返回值的问题了。

2.4.3 then 中回调函数的返回值

Promise 中 then 方法回调函数(即 onFulfilled 和 onRejected)的返回值可以分为两种情况:

  1. 普通值(即非 Promise 对象的值)。如果是这种情况,则直接将值作为参数执行 resolve 方法,即下一个链式调用的 then 中的 onFulfilled 回调函数会接收到这个值(注意,即使是 onRejected 返回了普通值,那也是调用 resolve 方法传递这个普通值,即不管是走到了 onFulfilled 还是 onRejected 函数,只要返回了普通值,那么返回的 Promise 对象状态一定是 FULFILLED);
  2. Promise 对象。如果是这种情况,则需要等待这个 Promise 对象的状态发生改变,如果这个 Promise 对象的状态最终变为 FULFILLED 且有值 value,则执行 resolve(value),如果状态最终为 REJECTED 且有值 reason,则执行 reject(reason)。
2.4.3.1 普通值

先来处理 onFulfilled 或者 onRejected 返回值是普通值的情况。直接将普通值作为参数执行 resolve 方法即可。

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";

  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  /**
   * 【私有变量】成功回调队列,存储多个 onFulfilled 回调函数
   */
  #resolveCallbacks = [];
  /**
   * 【私有变量】失败回调队列,存储多个 onRejected 回调函数
   */
  #rejectCallbacks = [];

  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
        // 当同步调用 resolve 时,#resolveCallbacks 为空
        // 主要是为了异步处理成功回调队列
        this.#resolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
        // 当同步调用 reject 时,#rejectCallbacks 为空
        // 主要是为了异步处理失败回调队列
        this.#rejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      switch (this.#status) {
        case MyPromise.PENDING:
          this.#resolveCallbacks.push(() => {
            // 执行回调函数时可能出错,所以需要捕获错误
            try {
              const x = onFulfilled(this.#value);
              resolve(x);
            } catch (err) {
              reject(err);
            }
          });
          this.#rejectCallbacks.push(() => {
            // 同上
            try {
              const x = onRejected(this.#reason);
              resolve(x);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case MyPromise.FULFILLED:
          // 同上
          try {
            const x = onFulfilled(this.#value);
            resolve(x);
          } catch (err) {
            reject(err);
          }
          break;
        case MyPromise.REJECTED:
          // 同上
          try {
            const x = onRejected(this.#reason);
            resolve(x);
          } catch (err) {
            reject(err);
          }
          break;
      }
    });

    return promise2;
  }
}

测试:

new MyPromise((resolve, reject) => {
  resolve(1);
})
.then((value) => {
    console.log("then1", value);
})
.then((value) => {
    console.log("then2", value);
})
.then((value) => {
    console.log("then3", value);
});

// 输出:
// then1 1
// then2 undefined
// then3 undefined

可以看到,示例中的 onFulfilled 回调函数的返回值正常的被下一个 then 方法的 onFulfilled 回调函数接收到了。

2.4.3.2 Promise 对象

当 onFulfilled 或者 onRejected 回调函数返回值是 Promise 对象时,逻辑相对比较复杂,因此我们这里改造之前的结构,把处理回调函数返回值的逻辑抽离到一个函数 resolveReturnValue 中:

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";

  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  /**
   * 【私有变量】成功回调队列,存储多个 onFulfilled 回调函数
   */
  #resolveCallbacks = [];
  /**
   * 【私有变量】失败回调队列,存储多个 onRejected 回调函数
   */
  #rejectCallbacks = [];

  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
        // 当同步调用 resolve 时,#resolveCallbacks 为空
        // 主要是为了异步处理成功回调队列
        this.#resolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
        // 当同步调用 reject 时,#rejectCallbacks 为空
        // 主要是为了异步处理失败回调队列
        this.#rejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    /**
     * 处理 onFulfilled 和 onRejected 回调函数的返回值
     */
    const resolveReturnValue = (x, promise2, resolve, reject) => {
      // 直接处理普通值
      resolve(x);
    };
    
    const promise2 = new MyPromise((resolve, reject) => {
      switch (this.#status) {
        case MyPromise.PENDING:
          this.#resolveCallbacks.push(() => {
            // 注意,这里使用 setTimeout 是为了在 promise2 初始化之后引用 promise2,不然会报错
            //(因为 setTimeout 的回调函数在同步代码执行完后执行)
            setTimeout(() => {
              // 执行回调函数时可能出错,所以需要捕获错误
              try {
                const x = onFulfilled(this.#value);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          this.#rejectCallbacks.push(() => {
            setTimeout(() => {
              try {
                const x = onRejected(this.#reason);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          break;
        case MyPromise.FULFILLED:
          setTimeout(() => {
            try {
              const x = onFulfilled(this.#value);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case MyPromise.REJECTED:
          setTimeout(() => {
            try {
              const x = onRejected(this.#reason);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
      }
    });

    return promise2;
  }
}

接下来,我们开始实现 resolveReturnValue 函数的功能。这个函数主要有以下几个功能点:

  1. 处理返回值是 Promise 对象,但是这个 Promise 是 promise2 本身的问题(会引发死循环);
  2. 处理返回值是正常的 Promise 对象的问题;
  3. 处理返回值是普通值的情况。

key1: 返回 Promise 对象是上一个 Promise(链式循环引用)

先来看一个例子:

const p = new Promise((resolve, reject) => {
  resolve("ok");
}).then((value) => {
  return p;
});

// 输出:
// [TypeError: Chaining cycle detected for promise #<Promise>]

上面的例子中,then 方法会返回一个 Promise 对象,也就是变量 p,而在 then 的 onFulfilled 回调中返回值就是 p,也就是需要等待 p 的结果才能确定 p 的状态,这就陷入了死循环。所以,为了避免这种情况就需要对返回值进行判断,如果返回值就是 promise2,则需要抛出异常:

    const resolveReturnValue = (x, promise2, resolve, reject) => {
      // 处理链式循环引用问题(返回的 Promise 对象是上一个 Promise)
      if (x === promise2) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }
      // 处理普通值
      resolve(x);
    };

测试:

const p = new MyPromise((resolve, reject) => {
  resolve("ok");
}).then((value) => {
  return p;
});

p.then(
  () => {},
  (reason) => {
    console.log(reason);
  }
);

// 输出:
// TypeError: Chaining cycle detected for promise #<Promise>

结果符合预期。

这里需要注意变量 p 的引用,来看下面这个例子:

const p = new MyPromise((resolve, reject) => {
  resolve("ok");
})
  .then((value) => {
    return p;
  })
  .then(
    () => {},
    (reason) => {
      console.log(reason);
    }
  );

以上代码执行后不会有任何输出,这是因为变量 p 是第二个 then 返回的 Promise 对象,所以在第一个 then 方法的判断中,promise2 是第一个 then 返回的 Promise 对象,而 x 是 p (也就是第二个 then 返回的 Promise 对象),因此 x !== promise2,所以不会触发链式循环引用的报错。

key2: 返回正常的 Promise 对象

首先,我们要知道如何判断一个变量是不是 Promise 对象,根据 Promises/A+ 规范 的定义:如果一个变量是对象或者函数并且有 then 方法,则视为一个 promise 对象。

    /**
     * 处理 onFulfilled 和 onRejected 回调函数的返回值
     */
    const resolveReturnValue = (x, promise2, resolve, reject) => {
      // 处理链式循环引用问题(返回的 Promise 对象是上一个 Promise)
      if (x === promise2) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }

      // 处理 Promise 对象
      // 如果一个变量是对象或者函数并且有 then 方法,则视为一个 promise 对象
      if (x && (typeof x === "object" || typeof x === "function")) {
        try {
          // 确保在调用 then 方法时使用的是最初的引用,
          // 而不会受到后续对 x 的修改或访问器属性的影响
          const then = x.then;
          if (typeof then === "function") {
            // 将 then 中的 this 绑定到 x
            then.call(
              x,
              (value) => {
                // 如果成功,递归调用(处理仍然返回 Promise 对象的情况)
                resolveReturnValue(value, promise2, resolve, reject);
              },
              (reason) => {
                // 如果失败,转为失败态
                reject(reason);
              }
            );
          } else {
            // 处理普通值(是对象或者函数,但没有 then 方法)
            resolve(x);
          }
        } catch (err) {
          // 调用 then 方法可能出错,需要捕获异常
          reject(err);
        }
      } else {
        // 处理普通值
        resolve(x);
      }
    };

注意,这里判断是不是 Promise 对象本质上是判断是不是 thenable 对象(thenable 指的是一个变量是对象或者函数并且具有 then 方法)。

因此,就会带来不确定性。比如,这个对象可能是用户自己实现的一个 Promise,没有严格遵循 Promises/A+ 规范,用户可能在执行器函数里多次调用了 resolve 或者 reject,导致 then 方法里的 onFulfilled 或者 onRejected 回调函数多次触发。举个例子:

const thenable2 = {
  then(onFulfilled) {
    setTimeout(() => {
      onFulfilled(2);
    }, 1000);
  },
};

const thenable1 = {
  then(onFulfilled) {
    onFulfilled(thenable2);
    onFulfilled(1);
  },
};

thenable1 的 then 方法执行了两次 onFulfilled 回调函数,因为第一个 onFulfilled 的参数是 thenable2 ,而 thenable2 的值需要等待定时器计时完成,因此第二个 onFulfilled 会先执行,也就是会 resolve(1)。

然而,根据 Promises/A+ 规范,当有多个 onFulfilled 或者 onRejected 回调函数执行时,取第一个回调函数的执行结果。显然,上面的实现不符合预期。

这里,为了解决这个问题,引入了 called 变量,如果 called 为 true,说明 then 的回调函数已经执行,则不再执行后续的回调函数。

    /**
     * 处理 onFulfilled 和 onRejected 回调函数的返回值
     */
    const resolveReturnValue = (x, promise2, resolve, reject) => {
      // 处理链式循环引用问题(返回的 Promise 对象是上一个 Promise)
      if (x === promise2) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }

      // 处理 Promise 对象
      // 如果一个变量是对象或者函数并且有 then 方法,则视为一个 promise 对象
      if (x && (typeof x === "object" || typeof x === "function")) {
        // 解决 promise 对象中实现的 then 方法可能重复调用 resolve 或 reject
        // 如果不加 called 进行条件过滤,会使得这些重复调用可能以最后一次调用作为结果
        // 根据 Promises/A+ 规范,应当以第一次调用作为结果,所以需要加上 called 过滤
        let called = false;
        try {
          // 确保在调用 then 方法时使用的是最初的引用,
          // 而不会受到后续对 x 的修改或访问器属性的影响
          const then = x.then;
          if (typeof then === "function") {
            // 将 then 中的 this 绑定到 x
            then.call(
              x,
              (value) => {
                // 如果回调函数已经调用过,则不再执行后续回调
                if (called) return;
                called = true;
                // 如果成功,递归调用(处理仍然返回 Promise 对象的情况)
                resolveReturnValue(value, promise2, resolve, reject);
              },
              (reason) => {
                // 如果回调函数已经调用过,则不再执行后续回调
                if (called) return;
                called = true;
                // 如果失败,转为失败态
                reject(reason);
              }
            );
          } else {
            // 此时没有 then 方法,所以肯定不会执行回调函数,所以不考虑多次调用的情况,因此不加 called 过滤
            // 处理普通值(是对象或者函数,但没有 then 方法)
            resolve(x);
          }
        } catch (err) {
          // 如果回调函数已经调用过,则不再执行后续回调(回调函数执行过程可能出错走到这里)
          if (called) return;
          called = true;
          // 调用 then 方法可能出错,需要捕获异常
          reject(err);
        }
      } else {
        // 处理普通值
        resolve(x);
      }
    };

key3: 返回普通值

在上面的代码中已经实现了这种情况,即 x 不是对象和函数或者是对象或函数但是没有 then 方法。

2.4.4 then 回调函数透传

then 方法里的 onFulfilled 和 onRejected 回调可能用户没传或者不为函数,此时需要转换这两个回调函数,默认为透传传入的 value 或者 error。

  then(onFulfilled, onRejected) {
    // onFulfilled 应当是函数,接收成功的 value,如果不是函数,则将其转换为函数,
    // 用于透传 value,交给下一个 onFulfilled 处理
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    // onRejected 应当是函数,接收失败的 reason,如果不是函数,则将其转换为函数,
    // 用于透传错误 reason,交给下一个 onRejected 处理
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };
          
    // ...
 }

2.4.4 then 实现微任务

在上面实现的 then 方法中,我们为了能够在 promise2 初始化之前引用 promise2 的值,使用了 setTimeout API 把调用过程放到了定时器触发线程中运行。然而,setTimeout 触发的是一个宏任务,而 Promise 的 then 方法触发的应当是一个微任务。

为了保持一致,使用 queueMicrotask 方法替代 setTimeout,因为 queueMicrotask 触发的是微任务。

    const promise2 = new MyPromise((resolve, reject) => {
      switch (this.#status) {
        case MyPromise.PENDING:
          this.#resolveCallbacks.push(() => {
            // 注意,这里使用 setTimeout 是为了在 promise2 初始化之后引用 promise2,不然会报错
            //(因为 setTimeout 的回调函数在同步代码执行完后执行)
            queueMicrotask(() => {
              // 执行回调函数时可能出错,所以需要捕获错误
              try {
                const x = onFulfilled(this.#value);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          this.#rejectCallbacks.push(() => {
            queueMicrotask(() => {
              try {
                const x = onRejected(this.#reason);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          break;
        case MyPromise.FULFILLED:
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.#value);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case MyPromise.REJECTED:
          queueMicrotask(() => {
            try {
              const x = onRejected(this.#reason);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
      }
    });

2.5 测试

至此,Promises/A+ 规范的实现基本上完成了。为了保证功能的完整性,我们这里使用 promises-aplus-tests 包进行测试。

  1. 安装测试包:npm install promises-aplus-tests
  2. 在文件末尾加入测试代码:
// promises-aplus-tests
MyPromise.deferred = function () {
  const deferred = {};
  deferred.promise = new MyPromise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
};

const promisesAPlusTests = require("promises-aplus-tests");

promisesAPlusTests(MyPromise, (err) => {
  if (err) {
    console.log("测试用例执行失败", err);
  } else {
    console.log("测试用例执行成功");
  }
});

module.exports = MyPromise;
  1. 执行文件进行测试:node ./MyPromise.js
  2. 检查代码是否测试通过

image.png

2.6 完整代码

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";

  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  /**
   * 【私有变量】成功回调队列,存储多个 onFulfilled 回调函数
   */
  #resolveCallbacks = [];
  /**
   * 【私有变量】失败回调队列,存储多个 onRejected 回调函数
   */
  #rejectCallbacks = [];

  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
        // 当同步调用 resolve 时,#resolveCallbacks 为空
        // 主要是为了异步处理成功回调队列
        this.#resolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
        // 当同步调用 reject 时,#rejectCallbacks 为空
        // 主要是为了异步处理失败回调队列
        this.#rejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    // onFulfilled 应当是函数,接收成功的 value,如果不是函数,则将其转换为函数,
    // 用于透传 value,交给下一个 onFulfilled 处理
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    // onRejected 应当是函数,接收失败的 reason,如果不是函数,则将其转换为函数,
    // 用于透传错误 reason,交给下一个 onRejected 处理
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    /**
     * 处理 onFulfilled 和 onRejected 回调函数的返回值
     */
    const resolveReturnValue = (x, promise2, resolve, reject) => {
      // 处理链式循环引用问题(返回的 Promise 对象是上一个 Promise)
      if (x === promise2) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }

      // 处理 Promise 对象
      // 如果一个变量是对象或者函数并且有 then 方法,则视为一个 promise 对象
      if (x && (typeof x === "object" || typeof x === "function")) {
        // 解决 promise 对象中实现的 then 方法可能重复调用 resolve 或 reject
        // 如果不加 called 进行条件过滤,会使得这些重复调用可能以最后一次调用作为结果
        // 根据 Promises/A+ 规范,应当以第一次调用作为结果,所以需要加上 called 过滤
        let called = false;
        try {
          // 确保在调用 then 方法时使用的是最初的引用,
          // 而不会受到后续对 x 的修改或访问器属性的影响
          const then = x.then;
          if (typeof then === "function") {
            // 将 then 中的 this 绑定到 x
            then.call(
              x,
              (value) => {
                // 如果回调函数已经调用过,则不再执行后续回调
                if (called) return;
                called = true;
                // 如果成功,递归调用(处理仍然返回 Promise 对象的情况)
                resolveReturnValue(value, promise2, resolve, reject);
              },
              (reason) => {
                // 如果回调函数已经调用过,则不再执行后续回调
                if (called) return;
                called = true;
                // 如果失败,转为失败态
                reject(reason);
              }
            );
          } else {
            // 此时没有 then 方法,所以肯定不会执行回调函数,所以不考虑多次调用的情况,因此不加 called 过滤
            // 处理普通值(是对象或者函数,但没有 then 方法)
            resolve(x);
          }
        } catch (err) {
          // 如果回调函数已经调用过,则不再执行后续回调(回调函数执行过程可能出错走到这里)
          if (called) return;
          called = true;
          // 调用 then 方法可能出错,需要捕获异常
          reject(err);
        }
      } else {
        // 处理普通值
        resolve(x);
      }
    };

    const promise2 = new MyPromise((resolve, reject) => {
      switch (this.#status) {
        case MyPromise.PENDING:
          this.#resolveCallbacks.push(() => {
            // 注意,这里使用 setTimeout 是为了在 promise2 初始化之后引用 promise2,不然会报错
            //(因为 setTimeout 的回调函数在同步代码执行完后执行)
            queueMicrotask(() => {
              // 执行回调函数时可能出错,所以需要捕获错误
              try {
                const x = onFulfilled(this.#value);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          this.#rejectCallbacks.push(() => {
            queueMicrotask(() => {
              try {
                const x = onRejected(this.#reason);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          break;
        case MyPromise.FULFILLED:
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.#value);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case MyPromise.REJECTED:
          queueMicrotask(() => {
            try {
              const x = onRejected(this.#reason);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
      }
    });

    return promise2;
  }
}

// promises-aplus-tests
MyPromise.deferred = function () {
  const deferred = {};
  deferred.promise = new MyPromise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
};

const promisesAPlusTests = require("promises-aplus-tests");

promisesAPlusTests(MyPromise, (err) => {
  if (err) {
    console.log("测试用例执行失败", err);
  } else {
    console.log("测试用例执行成功");
  }
});

module.exports = MyPromise;

三、实现其他方法

到这里,我们已经实现了 Proimse 的核心功能。其实,Promise 的 then 方法就是核心部分,剩余的其他方法都可以由 then 方法实现。接下来,我们将实现剩余的方法。

  • 实例方法
    • then
    • catch
    • finally
  • 静态方法
    • resolve
    • reject
    • all
    • race
    • any
    • allSettled

3.1 实例方法

3.1.1 catch

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

  /**
   * 用于指定发生错误时的回调函数
   */
  catch(onRejected) {
    return this.then(null, onRejected);
  }

3.1.2 finally

  /**
   * 无论成功或失败都会调用,无法获取到成功的 value 和失败的 reason
   * 返回一个 promise 对象,透传上一次的 value 或 reason
   */
  finally(fn) {
    // 收集成功和失败回调
    return this.then(
      (value) => {
        // 执行 fn 并透传上一次的成功 value
        return MyPromise.resolve(fn()).then(() => value);
      },
      (reason) => {
        // 执行 fn 并透传上一次的失败 reason
        return MyPromise.resolve(fn()).then(() => {
          throw reason;
        });
      }
    );
  }

3.2 静态方法

3.2.1 resolve

  /**
   * 将一个对象(可能是 Promise,也可能是 thenable 对象,也可能是其他)转为 Promise 对象
   */
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

3.2.2 reject

  /**
   * Promise.reject() 方法的参数,会原封不动地作为 reject 的理由,变成后续方法的参数。
   */
  static reject(reason) {
    return new Promise((_resolve, reject) => {
      reject(reason);
    });
  }

3.2.3 all

  /**
   * 将多个 promise 放在一个数组中,
   * 当整个数组的全部 promise 成功时才会返回成功(返回的是所有成功结果的数组),
   * 当数组中的 promise 有一个出现失败时就返回失败 (失败的原因是第一个失败promise的结果)。
   * 返回的是一个promise
   */
  static all(promises) {
    // 判断传入的参数是否可迭代
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      const result = [];
      let times = 0;

      const processSuccess = (value, index) => {
        result[index] = value;

        // 全部执行成功后,返回数组
        if (++times === promises.length) {
          resolve(result);
        }
      };

      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          // 如果是 promises,则收集回调,成功则存入数组,失败则直接 reject
          p.then(
            (res) => {
              processSuccess(res, i);
            },
            (reason) => {
              reject(reason);
            }
          );
        } else {
          // 如果不是 promise,则直接存入数组
          processSuccess(p, i);
        }
      }
    });
  }

  static #isIterator(data) {
    if (!data || typeof data[Symbol.iterator] !== "function") {
      throw new TypeError(
        `${typeof data} ${data} is not iterable (cannot read property Symbol(Symbol.iterator))`
      );
    }
  }

3.2.4 race

  /**
   * 传入一个 promises 数组
   * 当其中一个 promise 首先转变状态时,返回其结果(无论成功或失败)
   * 当元素不是 promise 时,直接返回该元素
   */
  static race(promises) {
    // 判断传入的参数是否可迭代
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          p.then(resolve, reject);
        } else {
          resolve(p);
        }
      }
    });
  }

3.2.5 any

  /**
   * 传入一个 promises 数组
   * 当其中一个 promise 首先变为成功态时,返回其结果
   * 当元素不是 promise 时,直接返回该元素
   */
  static any(promises) {
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      const result = [];
      let times = 0;

      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          p.then(resolve, (reason) => {
            result[i] = reason;
            if (++times === promises.length) {
              reject(new AggregateError(result, "All promises were rejected"));
            }
          });
        } else {
          resolve(p);
        }
      }

      // 如果是空数组,则返回异常
      if (times === 0) {
        reject(new AggregateError(result, "All promises were rejected"));
      }
    });
  }

3.2.6 allSettled

  /**
   * 传入一个 promises 数组
   * 只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
   */
  static allSettled(promises) {
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      const result = [];
      let times = 0;

      const processComplete = (data, index, status) => {
        result[index] = {
          status,
          ...data,
        };

        if (++times === promises.length) {
          resolve(result);
        }
      };

      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          p.then(
            (value) => {
              processComplete({ value }, i, MyPromise.FULFILLED);
            },
            (reason) => {
              processComplete({ reason }, i, MyPromise.REJECTED);
            }
          );
        } else {
          processComplete({ value: p }, i, MyPromise.FULFILLED);
        }
      }
    });
  }

四、完整代码

class MyPromise {
  /**
   * 等待态
   */
  static PENDING = "pending";
  /**
   * 成功态
   */
  static FULFILLED = "fulfilled";
  /**
   * 失败态
   */
  static REJECTED = "rejected";

  /**
   * 【私有变量】promise 的状态,初始为 pending
   */
  #status = MyPromise.PENDING;
  /**
   * 【私有变量】promise 转为成功态后传递的值
   */
  #value = null;
  /**
   * 【私有变量】promise 转为失败态后传递的值
   */
  #reason = null;
  /**
   * 【私有变量】成功回调队列,存储多个 onFulfilled 回调函数
   */
  #resolveCallbacks = [];
  /**
   * 【私有变量】失败回调队列,存储多个 onRejected 回调函数
   */
  #rejectCallbacks = [];

  constructor(executor) {
    /**
     * 调用 resolve 后立即将 promise 转为成功态并记录传递的 value
     */
    const resolve = (value) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.FULFILLED;
        this.#value = value;
        // 当同步调用 resolve 时,#resolveCallbacks 为空
        // 主要是为了异步处理成功回调队列
        this.#resolveCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    /**
     * 调用 reject 后立即将 promise 转为失败态并记录传递的 reason
     */
    const reject = (reason) => {
      if (this.#status === MyPromise.PENDING) {
        this.#status = MyPromise.REJECTED;
        this.#reason = reason;
        // 当同步调用 reject 时,#rejectCallbacks 为空
        // 主要是为了异步处理失败回调队列
        this.#rejectCallbacks.forEach((callback) => {
          callback();
        });
      }
    };

    try {
      // 同步执行传入的 executor
      executor(resolve, reject);
    } catch (err) {
      // 如果 executor 不是函数或者执行 executor 时发生错误,将错误交给 reject 处理
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    // onFulfilled 应当是函数,接收成功的 value,如果不是函数,则将其转换为函数,
    // 用于透传 value,交给下一个 onFulfilled 处理
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    // onRejected 应当是函数,接收失败的 reason,如果不是函数,则将其转换为函数,
    // 用于透传错误 reason,交给下一个 onRejected 处理
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    /**
     * 处理 onFulfilled 和 onRejected 回调函数的返回值
     */
    const resolveReturnValue = (x, promise2, resolve, reject) => {
      // 处理链式循环引用问题(返回的 Promise 对象是上一个 Promise)
      if (x === promise2) {
        return reject(
          new TypeError("Chaining cycle detected for promise #<Promise>")
        );
      }

      // 处理 Promise 对象
      // 如果一个变量是对象或者函数并且有 then 方法,则视为一个 promise 对象
      if (x && (typeof x === "object" || typeof x === "function")) {
        // 解决 promise 对象中实现的 then 方法可能重复调用 resolve 或 reject
        // 如果不加 called 进行条件过滤,会使得这些重复调用可能以最后一次调用作为结果
        // 根据 Promises/A+ 规范,应当以第一次调用作为结果,所以需要加上 called 过滤
        let called = false;
        try {
          // 确保在调用 then 方法时使用的是最初的引用,
          // 而不会受到后续对 x 的修改或访问器属性的影响
          const then = x.then;
          if (typeof then === "function") {
            // 将 then 中的 this 绑定到 x
            then.call(
              x,
              (value) => {
                // 如果回调函数已经调用过,则不再执行后续回调
                if (called) return;
                called = true;
                // 如果成功,递归调用(处理仍然返回 Promise 对象的情况)
                resolveReturnValue(value, promise2, resolve, reject);
              },
              (reason) => {
                // 如果回调函数已经调用过,则不再执行后续回调
                if (called) return;
                called = true;
                // 如果失败,转为失败态
                reject(reason);
              }
            );
          } else {
            // 此时没有 then 方法,所以肯定不会执行回调函数,所以不考虑多次调用的情况,因此不加 called 过滤
            // 处理普通值(是对象或者函数,但没有 then 方法)
            resolve(x);
          }
        } catch (err) {
          // 如果回调函数已经调用过,则不再执行后续回调(回调函数执行过程可能出错走到这里)
          if (called) return;
          called = true;
          // 调用 then 方法可能出错,需要捕获异常
          reject(err);
        }
      } else {
        // 处理普通值
        resolve(x);
      }
    };

    const promise2 = new MyPromise((resolve, reject) => {
      switch (this.#status) {
        case MyPromise.PENDING:
          this.#resolveCallbacks.push(() => {
            // 注意,这里使用 setTimeout 是为了在 promise2 初始化之后引用 promise2,不然会报错
            //(因为 setTimeout 的回调函数在同步代码执行完后执行)
            queueMicrotask(() => {
              // 执行回调函数时可能出错,所以需要捕获错误
              try {
                const x = onFulfilled(this.#value);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          this.#rejectCallbacks.push(() => {
            queueMicrotask(() => {
              try {
                const x = onRejected(this.#reason);
                resolveReturnValue(x, promise2, resolve, reject);
              } catch (err) {
                reject(err);
              }
            });
          });
          break;
        case MyPromise.FULFILLED:
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.#value);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
        case MyPromise.REJECTED:
          queueMicrotask(() => {
            try {
              const x = onRejected(this.#reason);
              resolveReturnValue(x, promise2, resolve, reject);
            } catch (err) {
              reject(err);
            }
          });
          break;
      }
    });

    return promise2;
  }

  /**
   * 用于指定发生错误时的回调函数
   */
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  /**
   * 无论成功或失败都会调用,无法获取到成功的 value 和失败的 reason
   * 返回一个 promise 对象,透传上一次的 value 或 reason
   */
  finally(fn) {
    // 收集成功和失败回调
    return this.then(
      (value) => {
        // 执行 fn 并透传上一次的成功 value
        return MyPromise.resolve(fn()).then(() => value);
      },
      (reason) => {
        // 执行 fn 并透传上一次的失败 reason
        return MyPromise.resolve(fn()).then(() => {
          throw reason;
        });
      }
    );
  }

  /**
   * 将一个对象(可能是 Promise,也可能是 thenable 对象,也可能是其他)转为 Promise 对象
   */
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  /**
   * Promise.reject() 方法的参数,会原封不动地作为 reject 的理由,变成后续方法的参数。
   */
  static reject(reason) {
    return new Promise((_resolve, reject) => {
      reject(reason);
    });
  }

  /**
   * 将多个 promise 放在一个数组中,
   * 当整个数组的全部 promise 成功时才会返回成功(返回的是所有成功结果的数组),
   * 当数组中的 promise 有一个出现失败时就返回失败 (失败的原因是第一个失败promise的结果)。
   * 返回的是一个promise
   */
  static all(promises) {
    // 判断传入的参数是否可迭代
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      const result = [];
      let times = 0;

      const processSuccess = (value, index) => {
        result[index] = value;

        // 全部执行成功后,返回数组
        if (++times === promises.length) {
          resolve(result);
        }
      };

      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          // 如果是 promises,则收集回调,成功则存入数组,失败则直接 reject
          p.then(
            (res) => {
              processSuccess(res, i);
            },
            (reason) => {
              reject(reason);
            }
          );
        } else {
          // 如果不是 promise,则直接存入数组
          processSuccess(p, i);
        }
      }
    });
  }

  /**
   * 传入一个 promises 数组
   * 当其中一个 promise 首先转变状态时,返回其结果(无论成功或失败)
   * 当元素不是 promise 时,直接返回该元素
   */
  static race(promises) {
    // 判断传入的参数是否可迭代
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          p.then(resolve, reject);
        } else {
          resolve(p);
        }
      }
    });
  }

  /**
   * 传入一个 promises 数组
   * 当其中一个 promise 首先变为成功态时,返回其结果
   * 当元素不是 promise 时,直接返回该元素
   */
  static any(promises) {
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      const result = [];
      let times = 0;

      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          p.then(resolve, (reason) => {
            result[i] = reason;
            if (++times === promises.length) {
              reject(new AggregateError(result, "All promises were rejected"));
            }
          });
        } else {
          resolve(p);
        }
      }

      // 如果是空数组,则返回异常
      if (times === 0) {
        reject(new AggregateError(result, "All promises were rejected"));
      }
    });
  }

  /**
   * 传入一个 promises 数组
   * 只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
   */
  static allSettled(promises) {
    MyPromise.#isIterator(promises);

    return new MyPromise((resolve, reject) => {
      const result = [];
      let times = 0;

      const processComplete = (data, index, status) => {
        result[index] = {
          status,
          ...data,
        };

        if (++times === promises.length) {
          resolve(result);
        }
      };

      for (let i = 0; i < promises.length; i++) {
        const p = promises[i];
        if (p && typeof p.then === "function") {
          p.then(
            (value) => {
              processComplete({ value }, i, MyPromise.FULFILLED);
            },
            (reason) => {
              processComplete({ reason }, i, MyPromise.REJECTED);
            }
          );
        } else {
          processComplete({ value: p }, i, MyPromise.FULFILLED);
        }
      }
    });
  }

  /**
   * 判断参数是否是可迭代的对象
   */
  static #isIterator(data) {
    if (!data || typeof data[Symbol.iterator] !== "function") {
      throw new TypeError(
        `${typeof data} ${data} is not iterable (cannot read property Symbol(Symbol.iterator))`
      );
    }
  }
}

// promises-aplus-tests
MyPromise.deferred = function () {
  const deferred = {};
  deferred.promise = new MyPromise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
};

const promisesAPlusTests = require("promises-aplus-tests");

promisesAPlusTests(MyPromise, (err) => {
  if (err) {
    console.log("测试用例执行失败", err);
  } else {
    console.log("测试用例执行成功");
  }
});

module.exports = MyPromise;

五、参考资料