手写Promise

手写Promise已经成为了非常常见的考题,因此来实现一个简易版的Promise,Let’s get it !

const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
  const that = this;
  that.state = PENDING;
  that.value = null; //用于存储resolve或者reject传入的值
  that.resolvedCallback = [];
  that.rejectedCallback = []; //用于保存then中的回调

  function resolve(value) {
    if (that.state === PENDING) {
      that.state = RESOLVED;
      that.value = value;
      that.resolvedCallback.map(cb => cb(that.value));
    }
  }

  function reject(value) {
    if (that.state === PENDING) {
      that.state = REJECTED;
      that.value = value;
      that.rejectedCallback.map(cb => cb(that.value));
    }
  }

  try {
    fn(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

//实现then方法
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : r => {
          throw r;
        };
  if (that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled);
    that.rejectedCallbacks.push(onRejected);
  }
  if (that.state === RESOLVED) {
    onFulfilled(that.value);
  }
  if (that.state === REJECTED) {
    onRejected(that.value);
  }
};

现在逐步拆解:

1~10行:

  • 首先我们创建了三个常量用于表示状态,对于经常使用的一些值都应该通过常量来管理,便于开发及后期维护
  • 在函数体内部首先创建了常量 that,因为代码可能会异步执行,用于获取正确的 this 对象
  • 一开始 Promise 的状态应该是 pending
  • value 变量用于保存 resolve 或者 reject 中传入的值
  • resolvedCallbacksrejectedCallbacks 用于保存 then 中的回调,因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用

12~26行:

  • 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
  • 将当前状态更改为对应状态,并且将传入的值赋值给 value
  • 遍历回调数组并执行

28~33行:

  • 实现很简单,执行传入的参数并且将之前两个函数当做参数传进去
  • 要注意的是,可能执行函数过程中会遇到错误,需要捕获错误并且执行 reject 函数

接下来实现then方法:

38~44行:

  • 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
  • 当参数不是函数类型时,需要创建一个函数赋值给对应的参数

45~55行:

接下来就是一系列判断状态的逻辑,当状态不是等待态时,就去执行相对应的函数。如果状态是等待态的话,就往回调函数中 push 函数,比如如下代码就会进入等待态的逻辑