记录一次手写 promise

背景

刷掘金的时候无意中看到手写 promise 的文章,之前一直因为觉得太难没有尝试过,正好最近有时间,完整的读了下来,但是很多地方看不懂,于是也想着自己试试

参考文章

https://juejin.cn/post/7339042131467616296icon-default.png?t=N7T8https://juejin.cn/post/7339042131467616296

https://juejin.cn/post/7346519210724786210icon-default.png?t=N7T8https://juejin.cn/post/7346519210724786210


Promises/A+icon-default.png?t=N7T8https://promisesaplus.com/

代码

/**
 * 2.2.2.3 它不能被调用多次
 * 2.2.3.3 它不能被调用多次
 * 2.2.7.2 如果抛出了异常,以 reason 为返回值,执行 reject
 */

// 2.1 三种状态
const Status = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected",
};
class XPromise {
  /**
   * 根据 promise 的使用,此处传入函数,参数为 resolve 和 reject
   * @param executor 执行器
   */
  constructor(executor) {
    // 维护一个状态变量
    this.status = Status.PENDING;
    // 维护值和原因
    this.value = undefined;
    this.reason = undefined;
    // 维护两个回调列表
    this.onFulfilledCbLst = [];
    this.onRejectedCbLst = [];
    if (typeof executor !== "function") {
      // 类型错误
      throw new Error(`executor 应为函数`);
    }
    // pending -> fulfilled
    const resolve = (value) => {
      if (this.status === Status.PENDING) {
        this.status = Status.FULFILLED;
        this.value = value;
        // 执行回调
        this.onFulfilledCbLst.forEach((fn) => fn(value));
      }
    };
    // pending -> rejected
    const reject = (reason) => {
      if (this.status === Status.PENDING) {
        this.status = Status.REJECTED;
        this.reason = reason;
        // 执行回调
        this.onRejectedCbLst.forEach((fn) => fn(reason));
      }
    };
    executor(resolve, reject);
  }
  /**
   * 2.2 then 方法,用于访问其当前或最终的值或原因
   * @param onFulfilled Function 可选
   * @param onRejected Function 可选
   */
  then(onFulfilled, onRejected) {
    const that = this;
    // 2.2.7 then 返回一个 promise
    let promise2 = new XPromise((resolve, reject) => {
      // 2.2.2.1 在满足之后调用,以 promise2 value 作为第一个参数
      function onFulfilledCb(value) {
        /**
         * 2.2.4 onFulfilled, onRejected 在执行上下文堆栈仅包含平台代码之前,不得调用
         * 根据 gpt 回答,要在微任务中调用,所以先加入队列
         */
        queueMicrotask(() => {
          try {
            const x = onFulfilled(value);
            that.promiseResolve(promise2, x, resolve, reject);
          } catch (error) {
            // 2.2.7.2 如果 onFulfilled/onRejected 抛出异常,以 error 为理由拒绝
            reject(error);
          }
        });
      }
      // 2.2.3.1 在被拒绝之后调用,以 promise2 reason 作为第一个参数
      function onRejectedCb(reason) {
        queueMicrotask(() => {
          try {
            const x = onRejected(reason);
            that.promiseResolve(promise2, x, resolve, reject);
          } catch (error) {
            // 2.2.7.2 如果 onFulfilled/onRejected 抛出异常,以 error 为理由拒绝
            reject(error);
          }
        });
      }
      // 2.2.2.2 onFulfilled 在实现之前不得调用
      if (this.status === Status.FULFILLED) {
        // 2.2.1.1 如果 onFulfilled 不是函数,忽略
        if (typeof onFulfilled === "function") {
          // 2.2.2.1 在满足之后调用,以 promise2 value 作为第一个参数
          onFulfilledCb(this.value);
        } else {
          // 2.2.7.3 如果 onFulfilled 不是函数,且 promise1 状态是 FULFILLED
          // promise2 以 promise1 相同的值变为已解决
          resolve(this.value);
        }
      } else if (this.status === Status.REJECTED) {
        // 2.2.3.2 onRejected 在被拒绝之前不得调用
        // 2.2.1.2 如果 onRejected 不是函数,忽略
        if (typeof onRejected === "function") {
          // 2.2.3.1 在被拒绝之后调用,以 promise2 reason 作为第一个参数
          onRejectedCb(this.reason);
        } else {
          // 2.2.7.4 如果 onRejected 不是函数,且 promise1 状态是 REJECTED
          // promise2 以 promise1 相同的reason变为已拒绝
          reject(this.reason);
        }
      } else {
        if (typeof onFulfilled === "function") {
          // 2.2.6.1 如果满足,onFulfilled 相应回调按对 then 发起调用的顺序执行
          this.onFulfilledCbLst.push((value) => {
            onFulfilledCb(value);
          });
        } else {
          // 2.2.7.3 如果 onFulfilled 不是函数,且 promise1 状态是 FULFILLED
          // promise2 以 promise1 相同的值变为已解决
          this.onFulfilledCbLst.push((value) => {
            resolve(value);
          });
        }
        if (typeof onRejected === "function") {
          // 2.2.6.2 如果拒绝,onRejected 相应回调按对 then 发起调用的顺序执行
          this.onRejectedCbLst.push((reason) => {
            onRejectedCb(reason);
          });
        } else {
          // 2.2.7.4 如果 onRejected 不是函数,且 promise1 状态是 REJECTED
          // promise2 以 promise1 相同的reason变为已拒绝
          this.onRejectedCbLst.push((reason) => {
            reject(reason);
          });
        }
      }
    });

    return promise2;
  }

  /**
   * 判断数据是否为纯 object【数组、null 会返回 false】
   * @param obj 要判断的数据
   */
  isObject(obj) {
    return Object.prototype.toString.call(obj) === "[object Object]";
  }

  /**
   * 解析过程,根据 promise a+ 步骤翻译
   * 2.2.7.1 如果返回一个 value,运行 promise 内部的 resolve 解析函数
   * 2.3 promise 解析过程是一个抽象操作,将 promise 和一个值作为输入
   * promise2 指的是调用 then 方法返回的 promise
   * x 指的是 then 方法的回调函数调用后得到的数据
   * resolve 和 reject 是 promise2 构造函数中的参数
   */
  promiseResolve(promise2, x, resolve, reject) {
    // console.log("promise2", promise2, "x", x);
    // 2.3.1 如果 x 和 promise2 引用同一个对象,以 typeError 作为理由拒绝
    if (x == promise2) {
      reject(new TypeError(`promise2 和 x 引用了同一对象`));
    } else if (x instanceof XPromise) {
      // 2.3.2 如果 x 是一个 promise
      if (x.status === Status.PENDING) {
        // 2.3.2.1 如果 x 处于待处理状态,promise 保持待处理状态,直到 x 已解决或已拒绝
        // promise2 要在回调函数得到的 promise 状态变更后再变更状态
        x.onFulfilledCbLst.push((value) => {
          // resolve(value);
          this.promiseResolve(promise2, value, resolve, reject);
        });
        x.onRejectedCbLst.push((reason) => {
          reject(reason);
        });
      } else if (x.status === Status.FULFILLED) {
        // 2.3.2.2 如果 x 已解决,promise 变更为已解决,值与 x 一致
        // resolve(x.value);
        this.promiseResolve(promise2, x.value, resolve, reject);
      } else {
        // 2.3.2.3 如果 x 已拒绝,promise 变更为已拒绝,reason 与 x 一致
        reject(x.reason);
      }
    } else if (typeof x === "function" || this.isObject(x)) {
      // 2.3.3 如果是个对象/函数
      // 2.3.3.1 let then be x.then
      try {
        const then = x.then;
        let called = false;
        if (typeof then === "function") {
          try {
            // 2.3.3.3.3 如果同时调用或多次调用,第一个调用优先,忽略后续
            if (!called) {
              /**
               * 2.3.3.3 如果 then 是个函数,call it with x as this,参数 resolvePromise rejectPromise
               * 其中
               * 2.3.3.3.1 如果 调用 resolvePromise,并赋值 y,执行 resolve(promise2, y)
               * 2.3.3.3.2 如果 调用 rejectPromise,并附原因 r,promise2 变更为拒绝,r 为reason
               * 说明:resolvePromise rejectPromise 对应 onFulfilled onRejected
               */
              then.call(
                x,
                (y) => {
                  if (!called) {
                    // 重新执行
                    // 根据测试用例可以看到,y 可能是个异步 promise
                    this.promiseResolve(promise2, y, resolve, reject);
                    called = true;
                  }
                },
                (error) => {
                  if (!called) {
                    // 2.3.3.3.3 如果同时调用或多次调用,第一个调用优先,忽略后续
                    reject(error);
                    called = true;
                  }
                }
              );
            }
          } catch (error) {
            // 2.3.3.3.4 如果调用 then 函数,报错
            // 2.3.3.3.4.1 如果 resolvePromise/rejectPromise 已被调用,忽略
            if (!called) {
              called = true;
              // 2.3.3.3.4.2 否则,promise2 变更为已拒绝,error 为 reason
              reject(error);
            }
          }
        } else {
          // 2.3.3.4 如果 then 不是函数,promise2 变更为 FULFILLED value 取 x
          resolve(x);
        }
      } catch (error) {
        // 2.3.3.2 如果检索 x.then 导致异常,则 promise2 以 error 为理由拒绝
        reject(error);
      }
    } else {
      // 2.3.4 如果 x 不是函数或对象,promise2 变更为 FULFILLED value 取 x
      resolve(x);
    }
  }
}

正文

https://www.yuque.com/tangxiaoxi-dfdne/aczo22/dghr8d1o4fk2evdb?singleDoc# 《手写 promise》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值