实现一个符合 Promises/A+ 规范的 Promise

970 阅读17分钟

前言

Promise 是一种异步编程的解决方案,可以使我们的代码更简单整洁。了解 Promise 原理可以帮助我们快速分析定位问题。本篇将带你从零到一逐步实现 Promise/A+ 规范。

初步实现

我们先来简单回顾一下 Promise 使用方式:

const p = new Promise(() => {
  console.log("1");
});

console.log("p: ", p);
//1
//p1:  Promise { <pending> }

从这个例子中,我们可以看到:

  • 可以通过 new Promise 来实例化 promise 对象。
  • 实例化时需传入执行器函数(executor)作为参数,执行器函数会立即调用。

如果不传递执行器函数则会报 TypeError

现在我们可以尝试写第一版了,为了避免与原生 Promise 冲突,我把我们实现的 Promise 命名为 Eventual。代表相同概念的术语还有 DelayDeferredFuture 等等,具体名字你可以按照自己的想法。

class Eventual {
  constructor(executor) {
    //实例化时会立即执行
    executor();
  }
}
const eventual = new Eventual(() => {
  console.log("1");
});
//1
//eventual:  Eventual {}

现在我们得到了一个 eventual 实例,传入的执行器函数也会立即执行,但仔细观察就会发现 eventual 是个空对象并没有任何状态这与我们的预期不符。

状态管理

Promise/A+ 规定一个 promise 必然处于以下三种状态之一:

  • 待定(Pending):初始状态,既没有被兑现,也没有被拒绝。
  • 已兑现(Fulfilled):意味着操作成功完成。
  • 已拒绝(Rejected):意味着操作失败。

状态只能由 Pending --> Fulfilled 或者 Pending --> RejectedFulfilledRejected不能相互切换,当然也不能在切换为Pending。也就是说状态切换是单向且不可逆的。

接下来我们来完善第一版:

class Eventual {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";
  //设置初始状态
  PromiseState = Eventual.PENDING;

  constructor(executor) {
    //实例化时会立即执行
    executor();
  }
}

const eventual = new Eventual(() => {
  console.log("1");
});
//1
//eventual:  Eventual { PromiseState: 'pending' }

我们定义了 3 种状态并使用 PromiseState 保存初始状态。

状态切换

现在 eventual 有了初始状态那么怎么来切换 promise 的状态呢?你可能会脱口而出 eventual.PromiseState = xxx 。原生中PromiseState是私有的只能在内部操作,来看看原生是怎么切换状态的:

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

console.log("p1", p1); //Promise {<fulfilled>: undefined}

const p2 = new Promise((resolve, reject) => {
  reject();
});
console.log("p2", p2); //Promise {<rejected>: undefined}

控制 promise 状态的切换是通过执行器的参数 resolvereject 实现的。

调用 resolve 会把状态切换为 Fulfilled,调用 reject 会把状态切换为 Rejected。另外调用 reject 也会抛出错误。

状态切换的实现如下:

class Eventual {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";
  //设置初始状态
  PromiseState = Eventual.PENDING;

  resolve = () => {
    if (this.PromiseState === Eventual.PENDING) {
      //切换状态为Fulfilled
      this.PromiseState = Eventual.FULFILLED;
    }
  };

  reject = () => {
    if (this.PromiseState === Eventual.PENDING) {
      //切换状态为Rejected
      this.PromiseState = Eventual.REJECTED;
    }
  };
  constructor(executor) {
    //实例化时会立即执行
    executor(this.resolve, this.reject);
  }
}

这里之所以使用箭头函数实现 resolve、reject 是因为如果是普通函数this 会指向 window(严格模式下是 undefined),会出现this指向错误。

接下来测试一下:

const p1 = new Eventual((resolve, reject) => {
  resolve();
});

console.log("p1", p1);

const p2 = new Eventual((resolve, reject) => {
  reject();
});

console.log("p2: ", p2);

const p3 = new Eventual((resolve, reject) => {
  resolve();
  reject();
});

console.log("p3: ", p3);

输出:

p1 Eventual {
  PromiseState: 'fulfilled',
  resolve: [Function: resolve],
  reject: [Function: reject]
}

p2:  Eventual {
  PromiseState: 'rejected',
  resolve: [Function: resolve],
  reject: [Function: reject]
}

p3:  Eventual {
  PromiseState: 'fulfilled',
  resolve: [Function: resolve],
  reject: [Function: reject]
}

结果管理

待定状态的 promise 对象要么会通过一个值被兑现,要么会通过一个原因(错误)被拒绝。默认值是 undefined

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

console.log("p1", p1); //p1 Promise {<fulfilled>: 'success'}

const p2 = new Promise((resolve, reject) => {
  reject("error");
});
console.log("p2", p2); //p2 Promise {<rejected>: 'error'}

被兑现的值或被拒绝的原因可以通过 resolve、reject 保存到 promise 的 PromiseResult 上以供将来then注册的后续处理程序所使用。和 PromiseState相同PromiseResult也是私有属性。

接下来实现结果管理:

class Eventual {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";
  //设置初始状态
  PromiseState = Eventual.PENDING;
  //保存被兑现的值或者被拒绝的理由默认为undefined
  PromiseResult = undefined;

  resolve = (value) => {
    if (this.PromiseState === Eventual.PENDING) {
      this.PromiseState = Eventual.FULFILLED;
      //保存兑现的值
      this.PromiseResult = value;
    }
  };

  reject = (reason) => {
    if (this.PromiseState === Eventual.PENDING) {
      this.PromiseState = Eventual.REJECTED;
      //保存被拒绝的原因
      this.PromiseResult = reason;
    }
  };
  constructor(executor) {
    //实例化时会立即执行
    executor(this.resolve, this.reject);
  }
}

我们再来测试一下:

const p1 = new Eventual((resolve, reject) => {
  resolve("success");
});

console.log("p1", p1);

const p2 = new Eventual((resolve, reject) => {
  reject("error");
});

console.log("p2: ", p2);

输出:

p1:  Eventual {
  PromiseState: 'fulfilled',
  PromiseResult: 'success',
  resolve: [Function: resolve],
  reject: [Function: reject]
}

p2:  Eventual {
  PromiseState: 'rejected',
  PromiseResult: 'error',
  resolve: [Function: resolve],
  reject: [Function: reject]
}

到目前为止基础实现就算完成啦 😀。

then

按照传统我们来回顾一下 then的使用方式:

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

p1.then((value) => {
  console.log(value);
});

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

p2.then(undefined, (reason) => {
  console.log(reason);
});
//success
//error

then 是用来为 promise 注册后续的处理程序,该方法最多接受两个参数 onFulfilledonRejected。这两个函数都是可选的,如果提供则会在 promise 被兑现或被拒绝时执行。

onFulfilled 和 onRejected

因为 onFulfilled 只会在 promise 被兑现时执行,所以只要判断当前 promise 处于相应的状态去执行 onFulfilled就好了。onRejected与之类似不多做赘述。

class Eventual {
  then(onFulfilled, onRejected) {
    //判断当前的状态如果是Fulfilled则执行注册的onFulfilled函数
    if (this.PromiseState === Eventual.FULFILLED) {
      onFulfilled(this.PromiseResult);
    }
    //判断当前的状态如果是Rejected则执行注册的onRejected函数
    if (this.PromiseState === Eventual.REJECTED) {
      onRejected(this.PromiseResult);
    }
  }
  constructor(executor) {
    //实例化时会立即执行
    executor(this.resolve, this.reject);
  }
}

我们来测试一下:

const p1 = new Eventual((resolve, reject) => {
  resolve("success");
});

p1.then((value) => {
  console.log(value);
});

const p2 = new Eventual((resolve, reject) => {
  reject("error");
});

p2.then(undefined, (reason) => {
  console.log(reason);
});
//success
//error

then 注册的回调被成功打印了出来。真是太对了哥,哥太对 😛。

回调保存

你以为上面的代码就没问题了吗 🧐?如果 resolve 是放在异步里执行的会怎样?

const eventual = new Eventual((resolve, reject) => {
  setTimeout(resolve, 1000, "success");
});

eventual.then((value) => {
  console.log(value);
});

执行上面的代码你会发现,居然啥也没有输出,来看看原生的行为:

const p1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, "success");
});

p1.then((value) => {
  console.log(value);
});
//success

两者的表现并不一致,我们的代码哪里出了问题?我们来简单分析下:

  • 当实例化时执行器函数会立即调用。执行器会创建一个定时器并在 1s 后把 eventual 状态切换为 Fulfilled。

  • 接着会执行 then 方法。该方法会在内部判断当前 eventual 的状态。此时 eventual 还处于 Pending ,onFulfilled 并不会执行。

  • 1s 之后 resolve 将 eventual 切换为 Fulfilled。

到这里代码就执行完了,然而当状态切换为 Fulfilled 时并没有任何地方去调用 onFulfilled 自然也就什么都不会输出了。

现在问题定位了,如何解决呢 🤔。解决方法也比较简单,当 then 执行时如果 eventual 还处于 Pending 就把回调保存起来等到 resolve 时再去调用就好了

class Eventual {
  //设置初始状态
  PromiseState = Eventual.PENDING;
  //保存被兑现的值或者被拒绝的理由默认为undefined
  PromiseResult = undefined;

  // 保存onFulfilled
  onFulfilledCallbacks = undefined;
  // 保存onRejected
  onRejectedCallbacks = undefined;

  resolve = (value) => {
    if (this.PromiseState === Eventual.PENDING) {
      this.PromiseState = Eventual.FULFILLED;
      //修改为兑现的值
      this.PromiseResult = value;
      this.onRejectedCallbacks && this.onRejectedCallbacks(value);
    }
  };

  reject = (reason) => {
    if (this.PromiseState === Eventual.PENDING) {
      this.PromiseState = Eventual.REJECTED;
      //修改为被拒绝的理由
      this.PromiseResult = reason;
      this.rejectedCallback && this.rejectedCallback(reason);
    }
  };

  then(onFulfilled, onRejected) {
    //...部分省略
    if (this.PromiseState === Eventual.PENDING) {
      this.onFulfilledCallbacks = onFulfilled;
      this.onRejectedCallbacks = onRejected;
    }
  }
  constructor(executor) {
    //实例化时会立即执行
    executor(this.resolve, this.reject);
  }
}

我们再来以第一个例子测试一下输出如下:

success

then 方法的多次调用

熟悉 promise 的同学知道,可以针对一个 promise 多次调用 then。

举个例子:

const eventual = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 2000);
});
eventual.then((value) => {
  console.log(1);
  console.log("resolve", value);
});

eventual.then((value) => {
  console.log(2);
  console.log("resolve", value);
});

eventual.then((value) => {
  console.log(3);
  console.log("resolve", value);
});

上面的代码会输出

1
value success
2
value success
3
value success

而我们的代码显然是不支持的,我们需要做点小小的改动去支持多次调用 then。

class Eventual {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";
  //设置初始状态
  PromiseState = Eventual.PENDING;
  //保存被兑现的值或者被拒绝的理由默认为undefined
  PromiseResult = undefined;
  // 保存onFulfilled
  onFulfilledCallbacks = [];
  // 保存onRejected
  onRejectedCallbacks = [];

  resolve = (value) => {
    if (this.PromiseState === Eventual.PENDING) {
      this.PromiseState = Eventual.FULFILLED;
      this.PromiseResult = value;
      this.onFulfilledCallbacks &&
        this.onFulfilledCallbacks.map((onFulfilled) => onFulfilled(value));
    }
  };

  reject = (reason) => {
    if (this.PromiseState === Eventual.PENDING) {
      this.PromiseState = Eventual.REJECTED;
      this.PromiseResult = reason;
      this.onRejectedCallbacks &&
        this.onRejectedCallbacks((onRejected) => onRejected(reason));
    }
  };

  then(onFulfilled, onRejected) {
    //...部分省略
    if (this.PromiseState === Eventual.PENDING) {
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
  }
  constructor(executor) {
    //实例化时会立即执行
    executor(this.resolve, this.reject);
  }
}

接下来测试一下:

const eventual = new Eventual((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 2000);
});
eventual.then((value) => {
  console.log(1);
  console.log("value", value);
});

eventual.then((value) => {
  console.log(2);
  console.log("value", value);
});

eventual.then((value) => {
  console.log(3);
  console.log("value", value);
});

输出:

1
value success
2
value success
3
value success

所有 then 中的回调函数都已经执行,已经实现then 方法的多次调用。

参数校验

前面我们提到了 then 接受两个函数作为参数。如果我们非要传递一个非函数类型会怎样?

举个例子:

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

p.then("str");

执行上面代码你会发现浏览器根本不甩你。

根据规范传递给 then 的任何非函数类型都会静默忽略。所谓忽略并不是什么都不做,对于 onFulfilled 会 return value 对于 onRejected 会 throw reason

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

现在参数校验也实现了,距离完整版又进了一大步。

异步实现

接下来我们从一道题切入来看看 promise 的执行顺序。

console.log(1);

let promise = new Promise((resolve, reject) => {
  console.log(2);
  resolve("3");
});

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

console.log(4);

上面的代码直观感觉应该是输出:

1
2
3
4

实际上却会输出:

1
2
4
3

造成这么违反直觉的原因是,onFulfilled 和 onRejected 必须要等到当前执行栈清空后才能被调用,换句话说它们是异步执行的。

Promise/A+ 规定了,实践中要确保 onFulfilledonRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用宏任务(macro-task)机制或者微任务(micro-task)机制来实现。事实上原生 promise 是通过微任务实现的。

接下来实现异步:

class Eventual {
  then(onFulfilled, onRejected) {
    if (this.PromiseState === Eventual.FULFILLED) {
      queueMicrotask(() => {
        onFulfilled(this.PromiseResult);
      });
    } else if (this.PromiseState === Eventual.REJECTED) {
      queueMicrotask(() => {
        onRejected(this.PromiseResult);
      });
    } else if (this.PromiseState === Eventual.PENDING) {
      this.onFulfilledCallbacks.push(() => {
        queueMicrotask(() => {
          onFulfilled(this.PromiseResult);
        });
      });
      this.onRejectedCallbacks.push(() => {
        queueMicrotask(() => {
          onRejected(this.PromiseResult);
        });
      });
    }
  }
}

我们在用上面的例子测试一下:

1
2
4
3

最后输出和原生 promise 一致。

链式调用

什么是链式调用呢举个例子:

const p = new Promise((resolve, reject) => {});
p.then(() => {
  console.log(1);
}).then(() => {
  console.log(2);
});

像上面这样可以new promise().then().then()这样的就称为链式调用。现在我们的代码是不支持链式调用的,因为我们在 then 里面没有返回任何东西。

Promise/A+ 2.2.7规定:

promise2 = promise1.then(onFulfilled, onRejected);

then 方法必须返回一个 promise 对象,这是实现链式调用的核心

根据 2.2.7 规范,我们来实现链式调用:

class Eventual {
  then(onFulfilled, onRejected) {
    return new Eventual((resolve, reject) => {
      if (this.PromiseState === Eventual.FULFILLED) {
        queueMicrotask(() => {
          onFulfilled(this.PromiseResult);
        });
      } else if (this.PromiseState === Eventual.REJECTED) {
        queueMicrotask(() => {
          onRejected(this.PromiseResult);
        });
      } else if (this.PromiseState === Eventual.PENDING) {
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            onFulfilled(this.PromiseResult);
          });
        });
        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            onRejected(this.PromiseResult);
          });
        });
      }
    });
  }
}

我们在 then 方法里面返回了一个 eventual 实例,这样就可以链式的注册后续处理函数。

虽然可以链式的注册后续处理函数,但是目前链式注册的回调通通都不会执行。因为我们在 then 里返回了新的 eventual 实例,这些实例都是 Pending 状态。链式注册的回调会注册在新的 eventual 实例上。状态不改变 onFulfilled、onRejected 自然也不会执行。

若想要 onFulfilled 执行 只需要把新的 promise 状态切换就可以了:

class Eventual {
  then(onFulfilled, onRejected) {
    return new Eventual((resolve, reject) => {
      if (this.PromiseState === Eventual.FULFILLED) {
        queueMicrotask(() => {
          onFulfilled(this.PromiseResult);
          resolve();
        });
      } else if (this.PromiseState === Eventual.REJECTED) {
        queueMicrotask(() => {
          onRejected(this.PromiseResult);
          resolve();
        });
      } else if (this.PromiseState === Eventual.PENDING) {
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            onFulfilled(this.PromiseResult);
            resolve();
          });
        });
        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            onRejected(this.PromiseResult);
            resolve();
          });
        });
      }
    });
  }
}

我们来测试一下:

const p = new Eventual((resolve) => {
  resolve("success");
});
p.then(() => {
  console.log(1);
}).then(() => {
  console.log(2);
});
//1
//2

我们来分析一下:

  • 实例化 eventual 时会立即调用执行器,执行 resolve 函数将 p 切换为 Fulfilled。

  • 接下来运行第一个 then 此方法是同步的,但是注册的方法是异步的。纵使现在 p 已然是 Fulfilled 也要等到全局代码执行完毕才能去执行注册的回调。then 会返回一个新的 eveventual 实例,状态为 Pending。

  • 接下来为新的 eveventual 注册回调。

  • 全局代码执行完毕,之后会取出第一个 then 注册的回调执行。

queueMicrotask(() => {
  onFulfilled(this.PromiseResult);
  resolve();
});

因为闭包的关系,可以在微任务中拿到新的 eventual 的 resolve 方法。将新的 eventual 状态切换为 Fulfilled,那么第二个 then 注册的 onFulfilled 就也会执行了。 实际上这部分工作是在 onFulfilled、onRejected 的返回值处理中进行的。

resolvePromise

关于 onFulfilled 和 onRejected 返回值的处理 Promise/A+ 洋洋洒洒写了一大片总结一下就是:

  • 2.3.1 如果 promisex 指向同一对象,以 TypeError 为据因拒绝执行 promise
/**
 *
 * @param  {promise} promise2 promise1.then方法返回的新的promise对象
 * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
 * @param  {[type]} resolve   promise2的resolve方法
 * @param  {[type]} reject    promise2的reject方法
 */

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

返回自己会造成循环引用。

  • 2.3.3 如果 x 为对象或者函数
    • x.then 赋值给 then ,如果读取发生异常,则直接让promise失败,失败原因就是抛出的错误,然后结束处理即可
    • 如果 then 是函数,将 x 作为函数的作用域 this 调用之
    • onFulfilled执行时,如果得到的数据是y,则执行[[Resolve]](promise, y)
    • onRejected执行时,如果得到的失败原因是error,则让promise变成失败状态,原因是error
    • 如果 resolvePromiserejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
function resolvePromise(promise2, x, resolve, reject) {
  if (x === promise2) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  if ((typeof x === "object" && x != null) || typeof x === "function") {
    let then;
    let called = false;

    try {
      // 把 x.then 赋值给 then
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
      return reject(error);
    }
    // 如果 then 是函数
    if (typeof then === "function") {
      // 将 x 作为函数的作用域 this 调用之
      // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
      then.call(
        x,
        (y) => {
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          // 如果 resolvePromise 和 rejectPromise 均被调用,
          // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
          if (called) return;
          called = true;
          resolve(promise2, y, resolve, reject);
        },
        (r) => {
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          if (called) return;
          called = true;
          reject(r);
        }
      );
    }
  }
}
  • 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise
function resolvePromise(promise2, x, resolve, reject) {
  if (x === promise2) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  if ((typeof x === "object" && x != null) || typeof x === "function") {
    let then;
    let called = false;

    try {
      // 把 x.then 赋值给 then
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
      return reject(error);
    }
    // 如果 then 是函数
    if (typeof then === "function") {
      // 将 x 作为函数的作用域 this 调用之
      // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
      then.call(
        x,
        (y) => {
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          // 如果 resolvePromise 和 rejectPromise 均被调用,
          // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
          if (called) return;
          called = true;
          resolve(promise2, y, resolve, reject);
        },
        (r) => {
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          if (called) return;
          called = true;
          reject(r);
        }
      );
    }
  } else {
    //如果 x 不为对象或者函数,以 x 为参数执行 promise
    resolve(x);
  }
}

2.3.3.3.4.2 如果调用 then 方法抛出了异常 e ,如果 resolvePromiserejectPromise 已经被调用,则忽略之。否则以 e 为据因拒绝 promise

function resolvePromise(promise2, x, resolve, reject) {
  // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  if (promise2 === x) {
    return reject(
      new TypeError("The promise and the return value are the same")
    );
  }

  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    let then;
    try {
      // 把 x.then 赋值给 then
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
      return reject(error);
    }

    // 如果 then 是函数
    if (typeof then === "function") {
      let called = false;
      // 将 x 作为函数的作用域 this 调用之
      // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
      try {
        then.call(
          x,
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          (y) => {
            // 如果 resolvePromise 和 rejectPromise 均被调用,
            // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            // 实现这条需要前面加一个变量called
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (error) {
        // 如果调用 then 方法抛出了异常 e:
        // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        if (called) return;
        // 否则以 e 为据因拒绝 promise
        reject(error);
      }
    }
  } else {
    // 如果 x 不为对象或者函数,以 x 为参数执行 promise
    resolve(x);
  }
}

2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise

function resolvePromise(promise2, x, resolve, reject) {
  // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  if (promise2 === x) {
    return reject(
      new TypeError("The promise and the return value are the same")
    );
  }

  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    let then;
    try {
      // 把 x.then 赋值给 then
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
      return reject(error);
    }

    // 如果 then 是函数
    if (typeof then === "function") {
      let called = false;
      // 将 x 作为函数的作用域 this 调用之
      // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
      try {
        then.call(
          x,
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          (y) => {
            // 如果 resolvePromise 和 rejectPromise 均被调用,
            // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            // 实现这条需要前面加一个变量called
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (error) {
        // 如果调用 then 方法抛出了异常 e:
        // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        if (called) return;
        // 否则以 e 为据因拒绝 promise
        reject(error);
      }
    } else {
      resolve(x);
    }
  } else {
    // 如果 x 不为对象或者函数,以 x 为参数执行 promise
    resolve(x);
  }
}

异常处理

我们先来看看异常处理,根据 2.2.7.2 规定。

如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e

class Eventual {
  then(onFulfilled, onRejected) {
    return new Eventual((resolve, reject) => {
      if (this.PromiseState === Eventual.FULFILLED) {
        queueMicrotask(() => {
          try {
            onFulfilled(this.PromiseResult);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.PromiseState === Eventual.REJECTED) {
        queueMicrotask(() => {
          try {
            onRejected(this.PromiseResult);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.PromiseState === Eventual.PENDING) {
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              onFulfilled(this.PromiseResult);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              onFulfilled(this.PromiseResult);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });
  }
}

顺便处理一下执行器抛出错误的情况:

class Eventual {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }
}

完整的 Promises/A+ 实现

到这里我们的 Eventual 已经完成了 Promises/A+规范

class Eventual {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";

  PromiseState = Eventual.PENDING;
  PromiseResult = undefined;
  // 存储成功回调函数
  onFulfilledCallbacks = [];
  // 存储失败回调函数
  onRejectedCallbacks = [];
  resolve = (value) => {
    if (this.PromiseState === Eventual.PENDING) {
     //切换状态为Fulfilled
      this.PromiseState = Eventual.FULFILLED;
      this.PromiseResult = value;
      //执行onFulfilled回调
      this.onFulfilledCallbacks &&
        this.onFulfilledCallbacks.map((onFulfilled) => onFulfilled(value));
    }
  };
  reject = (reason) => {
    if (this.PromiseState === Eventual.PENDING) {
      //切换状态为Rejected
      this.PromiseState = Eventual.REJECTED;
      this.PromiseResult = reason;
      //执行onRejected回调
      this.onRejectedCallbacks &&
        this.onRejectedCallbacks.map((onRejected) => onRejected(reason));
    }
  };

  then(onFulfilled, onRejected) {
    //非函数类型静默忽略
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };

    const promise2 = new Eventual((resolve, reject) => {
      let x;
      if (this.PromiseState === Eventual.FULFILLED) {
        //异步执行
        queueMicrotask(() => {
         //异常处理
          try {
            x = onFulfilled(this.PromiseResult);
            //onFulfilled返回值处理
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.PromiseState === Eventual.REJECTED) {
        queueMicrotask(() => {
          try {
            x = onRejected(this.PromiseResult);
            //onRejected返回值处理
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.PromiseState === Eventual.PENDING) {
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              x = onFulfilled(this.PromiseResult);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              x = onRejected(this.PromiseResult);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
    });

    return promise2;
  }
  constructor(executor) {
   //执行器函数异常处理
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }
}

function resolvePromise(promise, x, resolve, reject) {
  // 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  if (promise === x) {
    return reject(
      new TypeError("The promise and the return value are the same")
    );
  }

  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    let then;
    try {
      // 把 x.then 赋值给 then
      then = x.then;
    } catch (error) {
      // 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
      return reject(error);
    }

    // 如果 then 是函数
    if (typeof then === "function") {
      let called = false;
      // 将 x 作为函数的作用域 this 调用之
      // 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
      try {
        then.call(
          x,
          // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
          (y) => {
            // 如果 resolvePromise 和 rejectPromise 均被调用,
            // 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          // 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (error) {
        // 如果调用 then 方法抛出了异常 e:
        // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
        if (called) return;

        // 否则以 e 为据因拒绝 promise
        reject(error);
      }
    } else {
      // 如果 then 不是函数,以 x 为参数执行 promise
      resolve(x);
    }
  } else {
    // 如果 x 不为对象或者函数,以 x 为参数执行 promise
    resolve(x);
  }
}

Promise A+ 测试

如何证明我们写的 Eventual 符合 Promises/A+ 规范呢?当然是用promises-aplus-tests 测试一下。

安装 promises-aplus-tests:

npm install promises-aplus-tests -D

手写代码中加入 deferred

Eventual.deferred = function () {
  var result = {};
  result.promise = new Eventual(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
};

配置启动命令

{
  "scripts": {
    "test": "promises-aplus-tests index"
  }
}

开始测试

npm run test

最终结果

1650213021(1).jpg

总结

总什么结啊,累了毁灭吧。