手撕 Promise

Promise/A+规范

术语

  • promise 一个有 then 方法的对象或函数,行为符合本规范
  • thenable 一个定义了 then 方法的对象或函数
  • value 任何 JavaScript 的合法值
  • exception throw 语句抛出的值
  • reason 一个标识 promise 被拒绝的原因的值

promise 的状态

  • pending 只能由 pending 状态转换为其他两个状态
  • fulfilledvalue,不能再改变状态了。
  • rejectedreason,不能再改变状态了。

then 方法

then 方法有两个可选参数

  • onFulfilled, 在 promise 完成后被调用,
  • onRejected, 在 promise 被拒绝执行后调用
  • 只被调用一次

then 方法可以被调用多次 then 方法必须返回一个 promise,实现链式调用

promise 的解析过程

// promise的解析过程
function resolvePromise(promise, x) {
  // x 不允许与 promise 相同
  if (promise === x) {
    rejectedPromise(promise, new TypeError("cant be the same"));
    return;
  }
  // x 是 promise的情况下,根据x的状态决定promise的状态
  if (isPromise(x)) {
    if (x.status === statusMap.FULFILLED) {
      fulfilledPromise(promise, x.value);
      return;
    }
    if (x.status === statusMap.REJECTED) {
      rejectedPromise(promise, x.reason);
      return;
    }
    // 如果x还是pending状态,则需要等待x执行完毕,再决定promise的装忒
    if (x.status === statusMap.PENDING) {
      x.then(
        () => {
          fulfilledPromise(promise, x.value);
        },
        () => {
          rejectedPromise(promise, x.reason);
        }
      );
      return;
    }
    return;
  }
  // 如果x不是promise,是一个函数或者对象
  if (isObject(x) || isFunction(x)) {
    let then;
    let called = false;
    try {
      then = x.then;
    } catch (error) {
      // 如果x上没有then方法(即不符合Promise/A+规范),直接reject
      rejectedPromise(promise, error);
      return;
    }
    // 如果有then方法,需要保证then方法只能被执行一次。(通过called)
    if (isFunction(then)) {
      try {
        // 执行then方法
        then.call(
          x,
          (y) => {
            if (called) {
              return;
            }
            // 设置标签位为执行过
            called = true;
            // 设置promise的状态
            resolvePromise(promise, y);
          },
          (r) => {
            if (called) {
              return;
            }
            called = true;
            rejectedPromise(promise, r);
          }
        );
      } catch (error) {
        // 执行异常直接抛出错误
        if (called) {
          return;
        }
        called = true;
        rejectedPromise(promise, error);
      }
      return;
    } else {
      // 有then属性,但是then不是可执行函数,那么认为x就是当前的返回值
      fulfilledPromise(promise, x);
      return;
    }
  } else {
    // x不是对象或者函数,是某个具体值,直接fulfilled
    fulfilledPromise(promise, x);
    return;
  }
}

具体实现

// 步骤一:了解promise规范
// 步骤二:实现
// 步骤三:测试
const statusMap = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected",
};

// 将promise设置为fulfilled状态
function fulfilledPromise(promise, value) {
  // 只能从pending状态转换为其他状态
  if (promise.status !== statusMap.PENDING) {
    return;
  }
  promise.status = statusMap.FULFILLED;
  promise.value = value;
  runCbs(promise.fulfilledCbs, value);
}
// 将promise设置为rejected状态
function rejectedPromise(promise, reason) {
  // 只能从pending状态转换为其他状态
  if (promise.status !== statusMap.PENDING) {
    return;
  }
  promise.status = statusMap.REJECTED;
  promise.reason = reason;
  runCbs(promise.rejectedCbs, reason);
}
function runCbs(cbs, value) {
  cbs.forEach((cb) => cb(value));
}
function isFunction(fn) {
  return (
    Object.prototype.toString.call(fn).toLocaleLowerCase() ===
    "[object function]"
  );
}
function isObject(obj) {
  return (
    Object.prototype.toString.call(obj).toLocaleLowerCase() ===
    "[object object]"
  );
}
function isPromise(p) {
  return p instanceof Promise;
}
class Promise {
  constructor(fn) {
    this.status = statusMap.PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.fulfilledCbs = []; // then fulfilled callback
    this.rejectedCbs = []; // then rejected callback
    fn(
      (value) => {
        // 防止直接传进来一个thenalbe
        // fulfilledPromise(this, value);
        resolvePromise(this, value);
      },
      (reason) => {
        rejectedPromise(this, reason);
      }
    );
  }

  // 两个参数
  then(onFulfilled, onRejected) {
    const promise1 = this;
    const promise2 = new Promise(() => {});
    if (promise1.status === statusMap.FULFILLED) {
      if (!isFunction(onFulfilled)) {
        return promise1;
      }
      setTimeout(() => {
        try {
          const x = onFulfilled(promise1.value);
          resolvePromise(promise2, x);
        } catch (error) {
          rejectedPromise(promise2, error);
        }
      }, 0);
    }
    if (promise1.status === statusMap.REJECTED) {
      if (!isFunction(onRejected)) {
        return promise1;
      }
      setTimeout(() => {
        try {
          const x = onRejected(promise1.reason);
          resolvePromise(promise2, x);
        } catch (error) {
          rejectedPromise(promise2, error);
        }
      }, 0);
    }
    if (promise1.status === statusMap.PENDING) {
      onFulfilled = isFunction(onFulfilled)
        ? onFulfilled
        : (value) => {
            return value;
          };
      onRejected = isFunction(onRejected)
        ? onRejected
        : (err) => {
            throw err;
          };
      promise1.fulfilledCbs.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(promise1.value);
            resolvePromise(promise2, x);
          } catch (error) {
            rejectedPromise(promise2, error);
          }
        }, 0);
      });
      promise1.rejectedCbs.push(() => {
        setTimeout(() => {
          try {
            const x = onRejected(promise1.reason);
            resolvePromise(promise2, x);
          } catch (error) {
            rejectedPromise(promise2, error);
          }
        }, 0);
      });
    }
    return promise2;
  }
}

// 测试用到的钩子
Promise.deferred = function () {
  const deferred = {};
  deferred.promise = new Promise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
};

module.exports = Promise;