【手撕源码系列】150行代码实现属于你自己的Promsie

238 阅读15分钟

一、前言

在JavaScript的异步编程世界里,Promise是实现复杂逻辑的基石,但是相信大部分同学都没有深入了解过它的实现细节,虽然大多数情况下我们不会在面试中遇到类似手写promise这样的题目,但是通过实现自己的promise我们可以更好地提升自己对于异步编程的理解和编码功力。因此本文会带领大家从零开始,一步一步按照 PromiseA+ 规范实现一个自己的 Promise 类并通过官方测试工具 promises-aplus-test 来进行验证,那么话不多说,让我们开始吧~

二、代码实现

为了使思路更加清晰,减少模板代码的数量,本文中的Promise将使用 ES6 class 进行实现,同时我会将整个实现过程按照功能点进行拆分,每个部分会尽量按照:原生Promise使用方法 -> 规范要求 -> 代码实现 的顺序进行编写,如果大家发现有跟不上的地方,可以反复多看几遍或者返回上一个部分进行复习~

1. 定义初始结构

我们先来复习一下ES6中的Promise是如何初始化的

const p0 = new Promise((resolve, reject) => {
})

console.log(p0) // Promise {<pending>}

const p1 = new Promise((resolve, reject) => {
    resolve("Resolved");
    reject("Rejected");
})

console.log(p1) // Promise {<fulfilled>: 'Resolved'}

const p2 = new Promise((resolve, reject) => {
    reject("Rejected");
    resolve("Resolved");
})

console.log(p2) // Promise {<rejected>: 'Rejected'}

const p3 = new Promise((resolve, reject) => {
    throw new Error("Opps");
})

console.log(p3) // Promise {<rejected>: Error: Opps}

这里一共包含了两个知识点:首先是Promise类的初始化参数是一个函数,里面包含两个方法resolvereject,它们被用于改变promise实例的状态。其次就是promise实例一共有三种状态,分别是pending(未决议),fulfilled(已完成)和rejected(被拒绝)。这里我们看一下规范中对于promise的状态如何定义的:

A promise must be in one of three states: pending, fulfilled, or rejected.

  1. When pending, a promise:
    1. may transition to either the fulfilled or rejected state.
  2. When fulfilled, a promise:
    1. must not transition to any other state.
    2. must have a value, which must not change.
  3. When rejected, a promise:
    1. must not transition to any other state.
    2. must have a reason, which must not change.

简单来说就是promise实例创建后初始状态是pending,此时可以通过resolvereject方法改变实例的状态为fulfilledrejected,但是这种改变是不可逆的且只有第一次生效,状态转移图如下:

image.png

除此之外还有一个小细节,那就是初始化过程中如果抛出异常e,则promise会以e为原因被拒绝。

OK,有了以上信息我们就可以开始着手搭建Promise类的架子了,代码如下:

class MyPromise {
  // 状态枚举值
  static Pending = "pending";
  static Fulfilled = "fulfilled";
  static Rejected = "rejected";

  constructor(fn) {
    // 初始状态为 pending
    this.state = MyPromise.Pending;
    // 存储 promise 的最终值
    this.result = null;
    try {
      // 执行初始化函数, 使用 bind 避免 this 丢失
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      // 初始化过程中抛出异常时拒绝 promise
      this.reject(error);
    }
  }

  resolve(value) {
    // 只有pending状态下调用有效
    if (this.state === MyPromise.Pending) {
      this.state = MyPromise.Fulfilled;
      this.result = value;
    }
  }

  reject(reason) {
    // 只有pending状态下调用有效
    if (this.state === MyPromise.Pending) {
      this.state = MyPromise.Rejected;
      this.result = reason;
    }
  }
}

这里有几个细节需要注意下:

  1. 调用初始化函数fn时需要用try...catch包裹并在接收到异常时拒绝promise
  2. 传给fn的参数resolvereject需要使用bind和实例进行绑定,否则会出现this丢失

至此Promise的初始化工作就做好了,怎么样是不是很简单呢?那接下来我们就要编写promise的核心方法then了,大家可要跟紧了~

2. 实现 then

首先我们还是先来看一下ES6中的原生Promise是什么样的:

let p1 = new Promise((resolve, reject) => {
    resolve('Resolved 1');
    resolve('Resolved 2')
    reject('Rejected');
})

p1.then(
    result => {
        console.log('fulfilled', result);
    },
    reason => {
        console.log('rejected', reason.message);
    }
)
// 控制台输出 "fulfilled Resolved 1"

从这里我们可以看到then方法主要的作用就是用来注册promise实例状态改变回调函数的。接下来我们跟着规范一起看看then方法有哪些实现上需要注意的细节:

A promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected)

  1. Both onFulfilled and onRejected are optional arguments:
    1. If onFulfilled is not a function, it must be ignored.
    2. If onRejected is not a function, it must be ignored.

首先规定了then方法有两个可选参数分别对应成功回调和失败回调,如果有一个参数未提供,就忽略掉它。注意这里的忽略并不是指什么也不做,对于onFulfilled方法来说就是将value原封不动的返回;对于onRejected来说就是返回reasononRejected因为是错误分支,我们返回reason应该throw一个Error,至于为什么要这么做,后面就理解了,我们继续往后看:

  1. If onFulfilled is a function:

    1. it must be called after promise is fulfilled, with promise’s value as its first argument.
    2. it must not be called before promise is fulfilled.
    3. it must not be called more than once.
  2. If onRejected is a function,

    1. it must be called after promise is rejected, with promise’s reason as its first argument.
    2. it must not be called before promise is rejected.
    3. it must not be called more than once.

这一段主要描述的是对两个回调函数自身和调用时机的要求,基本也在上面的例子中有所体现,没有什么新东西,所以接下来我们就可以动手修改我们的代码了:

class MyPromise {
    // 省略其他部分...
+    then(onFulfilled, onRejected) {

+        onFulfilled =
+          typeof onFulfilled === "function" ? onFulfilled : (value) => value;

+        onRejected =
+          typeof onRejected === "function" ? onRejected : (reason) => reason;
        
+        if (this.state === MyPromise.Fulfilled) {
+          onFulfilled(this.state)
+        }

+        if (this.state === MyPromise.Rejected) {
+          onRejected(this.state)
+        }
+    }
}

3. 实现异步

到目前为止,我们的代码中还没有任何关于异步相关的逻辑,所以接下来的任务就是实现异步逻辑,规范中是这样定义的:

onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].

这句话的想要表达的意思就是:成功或失败的回调函数只能通过JS引擎来调用,或者说必须通过事件循环队列来调度。

JavaScript作为一门单线程语言,其异步能力是通过事件循环队列实现的,由于事件循环并非文章重点,因此这里就不再过多赘述了,有兴趣的同学可以看下这个视频 【熟肉 | 内核】深入JavaScript中的EventLoop_哔哩哔哩_bilibili

理论上讲,promise回调事件队列的实现可以使用宏任务也可以使用微任务,这里我们与ES6中的原生Promise保持一致,使用 queueMicrotask 方法创建微任务来实现回调事件队列,修改代码如下:

then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function" ? onRejected : (reason) => reason;

    if (this.state === MyPromise.Fulfilled) {
+      queueMicrotask(() => {
+        onFulfilled(this.state)
+      })
    }

    if (this.state === MyPromise.Rejected) {
+      queueMicrotask(() => {
+        onRejected(this.state)
+      })
    }
  }

4. 实现then的多次调用

在ES6中,then方法可以被多次调用,用来注册多个回调函数:

const p = new Promise((resolve, reject) => {
    resolve("Resolved");
})

p.then((value) => {console.log("callback 1", val)});
p.then((value) => {console.log("callbakc 2", val)});

// callback 1 Resolved
// callback 2 Resolved

对应规范内容如下:

then may be called multiple times on the same promise.

  1. If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
  2. If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.

也就是说同一个promise实例,可以反复调用then来注册回调函数,当回调函数被触发时,要求按照注册时的顺序依次执行回调。因此我们很容易就可以想到用数组来存储注册的回调函数,更新代码如下:

class MyPromise {
  static Pending = "pending";
  static Fulfilled = "fulfilled";
  static Rejected = "rejected";

  constructor(fn) {
    this.state = MyPromise.Pending;
    this.result = null;
  +  this.onFulfilledCallbacks = []; // 成功回调队列
  +  this.onRejectedCallbacks = [];  // 失败回调队列
    try {
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  resolve(value) {
    if (this.state === MyPromise.Pending) {
      this.state = MyPromise.Fulfilled;
      this.result = value;
      // 依次执行成功回调
+     this.onFulfilledCallbacks.forEach((cb) => {
+        cb();
+     });
    }
  }

  reject(reason) {
    if (this.state === MyPromise.Pending) {
      this.state = MyPromise.Rejected;
      this.result = reason;
      // 依次执行失败回调
+     this.onRejectedCallbacks.forEach((cb) => {
+       cb();
+     });
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function" ? onRejected : (reason) => reason;

    if (this.state === MyPromise.Fulfilled) {
       queueMicrotask(() => {
         onFulfilled(this.state);
       });
    }

    if (this.state === MyPromise.Rejected) {
       queueMicrotask(() => {
         onRejected(this.state);
       });
    }

+   if (this.state === MyPromise.Pending) {
      // 添加成功回调
+     this.onFulfilledCallbacks.push(() => {
+       queueMicrotask(() => onFulfilled(this.result));
+     });
      // 添加失败回调
+     this.onRejectedCallbacks.push(() => {
+       queueMicrotask(() => onRejected(this.result));
+     });
+   }
  }
}

5. 实现then的链式调用

OK,难点来了!ES6中的Promise是支持链式调用的,也就是说then方法返回的对象也应该是一个promise对象,例如下面的例子:

let p1 = new Promise((resolve, reject) => {
    resolve(10)
})
p1.then(res => {
    console.log('fulfilled', res);
    return 2 * res
}).then(res => {
    console.log('fulfilled', res)
})

// fulfilled 10
// fulfilled 20

老样子,我们来看看规范中是如何描述的:

then must return a promise [3.3].

promise2 = promise1.then(onFulfilled, onRejected);

  1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
  2. If either onFulfilled or onRejected throws an exception epromise2 must be rejected with e as the reason.
  3. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
  4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.

2,3,4 三条规则比较好理解,分别描述了回调函数执行过程中发生异常或回调函数缺失时,如何设置新返回的promise2对象的状态。而第一条就不是那么好理解了,由于then方法中return的值可能是一个thenable类型的值,这时我们需要对这个返回值执行一个[[Resolve]]操作,目的是提取出这个thenable的返回值。

这里我们先暂停解释下thenable的概念,thenable顾名思义就是实现了then方法的对象或者函数,由于promiseA+规范只是约定了promise类应该遵循的的行为和数据结构,没有规定实现方式,为了使我们自己实现的promise和其他人实现的实例间相互兼容,所以有了如下约定:

如果一个对象obj拥有then方法且看上去像一个 promise,我们的promise就应当尝试将obj视为一个我们自己实现的promise实例,复用obj的值和状态而不是将它视为一个普通对象。

这种 thenable 的特性使得 Promise 的实现更具有通用性,可以兼容各种不同的实现方式。

thenable概念和[[Resolve]]操作是promise实现过程中最难的部分,建议大家多看几遍规范原文,完全理解后再继续后面的编码。

这里我们暂时不考虑[[Resolve]]操作,先根据上面的规范,对我们的代码进行完善:

  then(onFulfilled, onRejected) {
-      onFulfilled =
-        typeof onFulfilled === "function" ? onFulfilled : (value) => value;
        
-      onRejected =
-        typeof onRejected === "function" ? onRejected : (reason) => reason;

       // then方法返回值必须是一个promise
+      const promise2 = new MyPromise((resolve, reject) => {
          if (this.state === MyPromise.Fulfilled) {
            queueMicrotask(() => {
               // 回调执行异常时,返回的 promise 状态为 rejected
+              try {
+                if (typeof onFulfilled === "function") {
+                  const x = onFulfilled(this.result);
                   // resolvePromise 操作
+                  resolvePromise(promise2, x, resolve, reject);
+                } else {
                   // onFulfilled 不为函数时,新 promise 的值与状态复用当前实例的
+                  resolve(this.result);
+                }
+              } catch (error) {
+                reject(error);
+              }
            });
          }

          if (this.state === MyPromise.Rejected) {
            queueMicrotask(() => {
               // 回调执行异常时,返回的 promise 状态为 rejected
+              try {
+                if (typeof onRejected === "function") {
+                  const x = onRejected(this.result);
+                  resolvePromise(promise2, x, resolve, reject);
+                } else {
                   // onRejected 不为函数时,新 promise 的值与状态复用当前实例的
+                  reject(this.result);
+                }
+              } catch (error) {
+                reject(error);
+              }
            });
          }

          if (this.state === MyPromise.Pending) {
            this.onFulfilledCallbacks.push(() => {
              queueMicrotask(() => { 
                // 回调执行异常时,返回的 promise 状态为 rejected
                try {
+                  if (typeof onFulfilled === "function") {
+                    const x = onFulfilled(this.result);
+                    resolvePromise(promise2, x, resolve, reject);
+                  } else {
                     // onFulfilled 不为函数时,新 promise 的值与状态复用当前实例的
+                    resolve(this.result);
+                  }
+                } catch (error) {
+                  reject(error);
+                }
              });
            });

            this.onRejectedCallbacks.push(() => {
              queueMicrotask(() => {
                 // 回调执行异常时,返回的 promise 状态为 rejected
+                try {
+                  if (typeof onRejected === "function") {
+                    const x = onRejected(this.result);
+                    resolvePromise(promise2, x, resolve, reject);
+                  } else {
                     // onRejected 不为函数时,新 promise 的值与状态复用当前实例的
+                    reject(this.result);
+                  }
+                } catch (error) {
+                  reject(error);
+                }
              });
            });
          }
    });

    return promise2;
  }

现在只剩下[[Resolve]]对应的resolvePromise方法没有实现了,接下来就让我们来一起会会它。

6. 实现[[Resolve]]抽象方法

首先让我们来看下这个[[Resolve]]方法到底做了哪些事情,下面是一个原生Promise的例子:

const a = new Promise((resolve) => {resolve(10)});
const b = new Promsie((resolve) => {resolve(20)});
const c = a.then(val => {return b});
c.then(val => val); // Promise {<fulfilled>: 20}

我们发现 a.then 中返回的是一个thenable对象 b ,而产生的新实例 c 中接收到的最终值是 b 携带的值 20 而不是thenable对象本身。这里就是[[Resolve]]方法发挥作用的地方了,它会尝试对thenable对象进行递归解封,直到获取到最内层的非thenable值。

image.png

实际上,如果只考虑我们自己的promise类型对象,我们只需要通过instanceof操作符判断实例是否属于我们自己的Promise类,如果是就递归调用[[Resolve]]方法直到获取到非thenable的返回值为止。但是为了兼容其他thenable对象,我们还需要编写一系列兼容代码,而这也正是[[Resolve]]方法麻烦的地方。

在上一节中,我们定义了[[Resolve]]方法的格式,这里先详细说明一下各个参数的意义:

/**
 * @param {*} newPromise  then方法返回的 promise2 对象 
 * @param {*} x           onFulfilled方法的返回值
 * @param {*} resolve     promise2 对象的resolve方法
 * @param {*} reject      promise2 对象的reject方法
 */
function resolvePromise(newPromise, x, resolve, reject) {}

promiseA+规范中对[[Resolve]]方法的实现有着详细的描述,虽然看起来很多,但是其实只要跟着步骤一步一步来,基本上不会遇到太大的问题,我们一条一条来看:

If promise and x refer to the same object, reject promise with a TypeError as the reason.

如果 promisex 是同一个对象时,为了防止出现无限递归的情况,这里要抛出一个TypeError

function resolvePromise(newPromise, x, resolve, reject) {
+    if (newPromise === x) {
+        throw new TypeError("Chaining cycle detected for promise");
+    }
}

接下来我们看 x 是一个promise实例的情况:

If x is a promise, adopt its state [3.4]:

If x is pending, promise must remain pending until x is fulfilled or rejected.

If/when x is fulfilled, fulfill promise with the same value.

If/when x is rejected, reject promise with the same reason.

这里要求 newPromise 复用 x 的状态和返回值,因此我们调用 x.then ,并针对两种回调分别进行处理:

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    throw new TypeError("Chaining cycle detected for promise");
  }

+  else if (x instanceof MyPromise) {
     // onFulfilled 回调时递归调用 resolvePromise,onReject 回调时直接复用 newPromise 的 reject
+    x.then((y) => resolvePromise(newPromise, y, resolve, reject), reject);
+  }
}

最后就是 xthanable对象和其他值的情况了,我们先来看前者:

Otherwise, if x is an object or function

Let then be x.then.

If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.

如果 x 是一个thenable对象,我们会尝试获取它的then属性,如果获取过程中发生异常 e 则调用reject拒绝 newPromise

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    throw new TypeError("Chaining cycle detected for promise");
  }

  if (x instanceof MyPromise) {
    x.then((y) => resolvePromise(newPromise, y, resolve, reject), reject);
  }

+  else if (x !== null && (typeof x === "object" || typeof x === "function")) {
+    let then;
+    try {
+      then = x.then;
+    } catch (error) {
+      reject(error);
+    }
+  }
}

这里解释下为什么访问 x.then 时有可能发生异常,这是由于 x 对象上的 then 有可能是一个Getter 方法,或者 x 本身是一个Proxy对象并且对get方法进行了拦截,此时x.then会调用Getter或代理函数,这个过程中是有可能出现异常的。

拿到then方法后我们就可以尝试解析thenable对象的状态和返回值了:

  1. If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:

    1. If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).

    2. If/when rejectPromise is called with a reason r, reject promise with r.

    3. If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.

    4. If calling then throws an exception e,

      1. If resolvePromise or rejectPromise have been called, ignore it.
      2. Otherwise, reject promise with e as the reason.

这里别看洋洋洒洒一大段,其实核心逻辑就是一个:"尝试调用then方法并且处理两种回调"

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    throw new TypeError("Chaining cycle detected for promise");
  }

  if (x instanceof MyPromise) {
    x.then((y) => resolvePromise(newPromise, y, resolve, reject), reject);
  }

  else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    let then;
    try {
      then = x.then;
    } catch (error) {
      reject(error);
    }

+    if (typeof then === "function") {
       // resolve 和 reject 只有第一次调用时有效
+      let isCalled = false;
+      try {
+        then.call(
+          x,
           // 处理 onFulfilled 回调
+          (y) => {
+            if (!isCalled) {
+              isCalled = true;
               // 递归调用 resolvePromise
+              resolvePromise(newPromise, y, resolve, reject);
+            }
+          },
           // 处理 onRejected 回调
+          (r) => {
+            if (!isCalled) {
+              isCalled = true;
+              reject(r);
+            }
+          }
+        );
+      } catch (error) {
         // 处理 then 调用异常
+        if (!isCalled) {
+          isCalled = true;
+          reject(error);
+        }
+      }
+    }
  }
}

到这里基本已经完成了,还剩下 x.then 不是函数以及 x 不是thenable对象两种情况,轻轻松松~

If then is not a function, fulfill promise with x.

If x is not an object or function, fulfill promise with x.

修改代码如下:

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    throw new TypeError("Chaining cycle detected for promise");
  }

  if (x instanceof MyPromise) {
    x.then((y) => resolvePromise(newPromise, y, resolve, reject), reject);
  }

  else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    let then;
    try {
      then = x.then;
    } catch (error) {
      reject(error);
    }

    if (typeof then === "function") {
      let isCalled = false;
      try {
        then.call(
          x,
          (y) => {
            if (!isCalled) {
              isCalled = true;
              resolvePromise(newPromise, y, resolve, reject);
            }
          },
          (r) => {
            if (!isCalled) {
              isCalled = true;
              reject(r);
            }
          }
        );
      } catch (error) {
        if (!isCalled) {
          isCalled = true;
          reject(error);
        }
      }
+    } else {
+      resolve(x);
+    }
+  } else {
+   resolve(x);
+  }
}

大功告成!下面是完整代码供大家参考

class MyPromise {
  static Pending = "pending";
  static Fulfilled = "fulfilled";
  static Rejected = "rejected";

  constructor(fn) {
    this.state = MyPromise.Pending;
    this.result = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = []; 
    try {
      fn(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  resolve(value) {
    if (this.state === MyPromise.Pending) {
      this.state = MyPromise.Fulfilled;
      this.result = value;
      this.onFulfilledCallbacks.forEach((cb) => {
        cb();
      });
    }
  }

  reject(reason) {
    if (this.state === MyPromise.Pending) {
      this.state = MyPromise.Rejected;
      this.result = reason;
      // 依次执行失败回调
      this.onRejectedCallbacks.forEach((cb) => {
        cb();
      });
    }
  }

  then(onFulfilled, onRejected) {
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === MyPromise.Fulfilled) {
        queueMicrotask(() => {
          try {
            if (typeof onFulfilled === "function") {
              const x = onFulfilled(this.result);
              resolvePromise(promise2, x, resolve, reject);
            } else {
              resolve(this.result);
            }
          } catch (error) {
            reject(error);
          }
        });
      }

      if (this.state === MyPromise.Rejected) {
        queueMicrotask(() => {
          try {
            if (typeof onRejected === "function") {
              const x = onRejected(this.result);
              resolvePromise(promise2, x, resolve, reject);
            } else {
              reject(this.result);
            }
          } catch (error) {
            reject(error);
          }
        });
      }

      if (this.state === MyPromise.Pending) {
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              if (typeof onFulfilled === "function") {
                const x = onFulfilled(this.result);
                resolvePromise(promise2, x, resolve, reject);
              } else {
                resolve(this.result);
              }
            } catch (error) {
              reject(error);
            }
          });
        });

        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              if (typeof onRejected === "function") {
                const x = onRejected(this.result);
                resolvePromise(promise2, x, resolve, reject);
              } else {
                reject(this.result);
              }
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });

    return promise2;
  }
}

function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    throw new TypeError("Chaining cycle detected for promise");
  }

  if (x instanceof MyPromise) {
    x.then((y) => {
      resolvePromise(newPromise, y, resolve, reject);
    }, reject);
  } else if (x !== null && (typeof x === "object" || typeof x === "function")) {
    let then;
    try {
      then = x.then;
    } catch (error) {
      reject(error);
    }

    if (typeof then === "function") {
      let isCalled = false;
      try {
        then.call(
          x,
          (y) => {
            if (!isCalled) {
              isCalled = true;
              resolvePromise(newPromise, y, resolve, reject);
            }
          },
          (r) => {
            if (!isCalled) {
              isCalled = true;
              reject(r);
            }
          }
        );
      } catch (error) {
        if (!isCalled) {
          isCalled = true;
          reject(error);
        }
      }
    } else {
      resolve(x);
    }
  } else {
    resolve(x);
  }
}

接下来我们可以使用官方提供的测试集来测试下我们的Promise类,首先安装测试库相关依赖:

npm i promises-aplus-tests

接下来在我们的代码文件中添加如下代码:

MyPromise.deferred = function () {
  let result = {};
  result.promise = new MyPromise((resolve, reject) => {
    result.resolve = resolve;
    result.reject = reject;
  });
  return result;
};

module.exports = MyPromise;

然后前往pacakge.json添加脚本命令(假设文件路径为src/index.js):

{
  "scripts": {
    "test": "promises-aplus-tests src/index.js"
  }
}

最后运行命令行:

npm run test

运行结果:

image.png

可以看到872个测试用例全部通过,完美收官~