用 Typescript 写一个 ToyPromise(符合 A+ 规范) Van Van♂️✨

194 阅读13分钟

更新日志:

  • 精简非完整代码示例(只保留 any 示例, 其它静态方法在完整代码示例里); 修正静态方法: any 在可迭代对象(如数组长度为 0)无任何迭代项时根据规范应该返回错误; 调整AggregateError 异常抛出的错误消息格式; 添加新的静态方法: withResolvers() (2024-01-27 03:09)
  • 修正一些错误拼写(2023-08-16 23:17)...
  • 添加静态方法 ToyPromise.resolve(aPromise) 返回幂等性说明, 更新文章标注(2023-08-28 02:44)

出发点

  • 围绕着手写 Promise 规范实现, 大家都喜欢一人写一个, 趁着假期摸鱼时间以及水友群也有提到, 我也参考多位大佬们已经总结过的自己也模仿() 写出一个, 在此满足规范实现的基础上, 做一些不是那么规范的事情 ♂️
  • 文章不会从头到尾再总结一遍, 因为有很多人已经总结的很好了, 所以尽可能省流, 而不复读机
  • PS: 实际业务中这么整的意义是啥我还暂时不清楚(木有工作 🥲 ...)
  • 会提到一些可能没怎么被注意到的 .all, .race. allSettled 这些方法的一些行为(如一个代理数组)
  • 不少 ts 我直接 copy 的 ts 标准内置声明(没毛病)
  • 以此屑文抛砖引玉, 如有错误, 还请指出(轻喷 😶‍🌫️)

置顶的参考链接

省流说明

  • 建议直接拉到后面看实现代码(胜过我这里写的不咋的文笔)
  • 详细的分析这里不会再写一遍, 可以参考我参考的链接 \to 大佬们写的已经非常详细
  • Promise 的内部状态可以简约概括为一个状态机: 可以从pending 到其它两个状态, 且一状态落地(fulfilled, rejected), 就不能更改, 规范定义了三种类型, 但这里为了好玩些, 所以不妨加个 aborted, 可以得到如下一个简单的枚举定义(aborted 末尾使用)
    enum ToyPromiseStatus {
      PENDING = "pending",
      FULFILLED = "fulfilled",
      REJECTED = "rejected",
      ABORTED = "aborted"
    }
    
  • Promise 的构造器是一个函数, 内部的两个 functor (即 resolve, reject) 由 Promise 内部提供个 executor 使用(描述不佳, 建议直接看代码), 如名称一般, 可以设置 promise 的状态和内部值(value/reason)
  • then 的调用相当于返回一个新的 Promise 实例 \to return new ToyPromise(...), 并根据 落地/待定 的状况来决定处理方式, 如果已经落地, 直接解决 (即如果 executor 里的 落地方法没放在 setTimeout 等的话, 那么状态已经被改变, 在 then 里取得时已经是落地状态), 如果是 pending, 那么表明 executor 里执行 resolve/reject 可能放在类似 setTimeout 的异步包装里, 此时可在内部设置两个队列分别保存两种可能触发的事件, 等 executor 里异步包装的 resolve/reject 执行时机到来后, 各自从队列里取出执行(当然, 谁先执行, 就代表状态落地), 且 then 的 onFulfilled 里返回的值可以在下一个 then 里获取, 且如果是 Thenable, 那么需要递归处理, 等待完成获取
  • 具体的微任务实现方式使用: queueMicrotask(VoidFunction) 模拟
  • catch, finally 实际上是预先签名版本(提前设置 then 的参数)
  • ...

代码

一个基础实现的自定义 Promise

  • 描述过于枯燥, 先看一个基础实现版(PS: 借鉴了参考文章里的代码实现和 ts 内置标准类声明)

    enum ToyPromiseStatus {
      PENDING = "pending",
      FULFILLED = "fulfilled",
      REJECTED = "rejected"
    }
    
    type ToyPromiseAwaited<T> = T extends null | undefined
      ? T
      : T extends object & { then(onfulfilled: infer F, ...args: infer _): any }
      ? F extends (value: infer V, ...args: infer _) => any
        ? ToyPromiseAwaited<V>
        : never
      : T;
    
    interface Thenable<T> {
      then<TResult1 = T, TResult2 = never>(
        onFulfilled?: ((value: T) => TResult1 | Thenable<TResult1>) | undefined | null,
        onRejected?: ((reason: any) => TResult2 | Thenable<TResult2>) | undefined | null
      ): Thenable<TResult1 | TResult2>;
    }
    
    type ExecutorType<T> = (resolve: (value: T) => void, reject: (reason: any) => void) => void;
    
    type ToyPromiseAllSettledResult<T> =
      | { status: "fulfilled"; value: T }
      | {
          status: "rejected";
          reason: any;
        };
    
    class ToyPromise<T> implements Thenable<T> {
      #state: ToyPromiseStatus;
    
      #value!: T;
    
      #reason: any;
    
      readonly #resolvedCallbacks: Array<(value: T) => void>;
    
      readonly #rejectedCallbacks: Array<(reason: any) => void>;
    
      public constructor(executor: ExecutorType<T>) {
        if (typeof executor !== "function") {
          throw new TypeError(`ToyPromise resolver #<${typeof executor}> is not a function`);
        }
    
        this.#state = ToyPromiseStatus.PENDING;
        this.#resolvedCallbacks = [];
        this.#rejectedCallbacks = [];
    
        try {
          executor(this.#resolve.bind(this), this.#reject.bind(this));
        } catch (reason: any) {
          this.#reject(reason);
        }
      }
    
      #resolve(value: T): void {
        // 处理循环引用
        if (<ToyPromise<T>>value === this) {
          this.#reject(new TypeError("Chaining cycle detected for promise #<OptionalPromise>"));
          return;
        }
        if (value instanceof Promise || value instanceof ToyPromise) {
          (<Thenable<T>>value).then(this.#resolve.bind(this), this.#reject.bind(this));
          return;
        }
        if (this.#state === ToyPromiseStatus.PENDING) {
          this.#value = value;
          this.#state = ToyPromiseStatus.FULFILLED;
          for (const cb of this.#resolvedCallbacks) {
            cb(this.#value);
          }
        }
      }
    
      #reject(reason: any): void {
        if (this.#state === ToyPromiseStatus.PENDING) {
          this.#reason = reason;
          this.#state = ToyPromiseStatus.REJECTED;
          for (const cb of this.#rejectedCallbacks) {
            cb(this.#reason);
          }
        }
      }
    
      static #processThenableOrNor<T>(
        promise: ToyPromise<T>,
        value: any,
        resolve: (value: T) => void,
        reject: (reason: any) => void
      ) {
        if (promise === value) {
          return reject(new TypeError(`Chaining cycle detected for promise #<OptionalPromise>`));
        }
        let isCall: boolean = false;
        if ((value && typeof value === "object") || typeof value === "function") {
          /*
            使用 try-catch 避免如下情况
            Object.defineProperty(obj, "then", {
              get() {
                throw new Error("msg xxx")
              }
            })
          */
          try {
            const thenFunctor: ((resolve: (value: T) => void, reject: (reason: any) => void) => any) | undefined | null =
              value["then"];
    
            if (typeof thenFunctor === "function") {
              thenFunctor.call(
                value,
                (y) => {
                  if (!isCall) {
                    isCall = true;
                    this.#processThenableOrNor(value, y, resolve, reject);
                  }
                },
                (r) => {
                  if (!isCall) {
                    isCall = true;
                    reject(r);
                  }
                }
              );
            } else {
              resolve!(value);
            }
          } catch (reason) {
            if (!isCall) {
              isCall = true;
              reject(reason);
            }
          }
        } else {
          resolve!(value);
        }
      }
    
      public then<E = T, R = never>(
        onFulfilled?: ((value: T) => E | Thenable<E>) | undefined | null,
        onRejected?: ((reason: any) => R | Thenable<R>) | undefined | null
      ): ToyPromise<E | R> {
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val as Thenable<E>;
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : (reason) => {
                throw reason;
              };
        const retPromise = new ToyPromise<E | R>((resolve, reject) => {
          switch (this.#state) {
            case ToyPromiseStatus.FULFILLED:
              queueMicrotask(() => {
                try {
                  ToyPromise.#processThenableOrNor(retPromise, onFulfilled!(this.#value), resolve, reject);
                } catch (reason) {
                  reject(reason);
                }
              });
              break;
            case ToyPromiseStatus.REJECTED:
              queueMicrotask((): void => {
                try {
                  ToyPromise.#processThenableOrNor(retPromise, onRejected!(this.#reason), resolve, reject);
                } catch (reason) {
                  reject(reason);
                }
              });
              break;
            case ToyPromiseStatus.PENDING:
              this.#resolvedCallbacks.push((value: T): void =>
                queueMicrotask((): void => {
                  try {
                    ToyPromise.#processThenableOrNor(retPromise, onFulfilled!(value), resolve, reject);
                  } catch (reason) {
                    reject(reason);
                  }
                })
              );
              this.#rejectedCallbacks.push((reason: any): void =>
                queueMicrotask((): void => {
                  try {
                    ToyPromise.#processThenableOrNor(retPromise, onRejected!(reason), resolve, reject);
                  } catch (reason) {
                    reject(reason);
                  }
                })
              );
              break;
            default:
              break;
          }
        });
    
        return retPromise;
      }
    }
    
  • 写的零零散散, 先简单测试一下(结果还算 OK)

    image.png

实现 catch, finally 以及静态方法

  • 这里省略前面已有代码...

    class ToyPromise<T> implements Thenable<T> {
      // 前面那些代码....
    
      public catch<TResult = never>(
        onRejected?: ((reason: any) => TResult | Thenable<TResult>) | undefined | null
      ): ToyPromise<T | TResult> {
        return this.then(null, onRejected) as any;
      }
    
      public finally(onFinally?: (() => void) | undefined | null): ToyPromise<T> {
        onFinally = typeof onFinally === "function" ? onFinally : () => {};
        return this.then(
          (value) => ToyPromise.resolve(onFinally!()).then(() => value),
          (reason) =>
            ToyPromise.resolve(onFinally!()).then(() => {
              throw reason;
            })
        );
      }
    
      static #isThenable(obj: any): boolean {
        if ((obj && typeof obj === "object") || typeof obj === "function") {
          return typeof obj["then"] === "function";
        }
        return false;
      }
    
      static #isIterable(obj: any): boolean {
        // 避免 "" 空字符(那么是 iterable) 被判断为 false 条件
        if (typeof obj === "string") {
          return true;
        }
        // 如果是 null 直接返回 false
        if (typeof obj === "object" && !obj) {
          return false;
        }
        // 判断是否为可迭代对象
        if (typeof obj === "function" || typeof obj === "object") {
          /*
              兼容如下这种示例情况
              function foo() {}
              foo[Symbol.iterator] = () => { yield 233 }
            */
          return typeof obj[Symbol.iterator] === "function";
        } else {
          return false;
        }
      }
    
      // 这里只举例 any, 其它静态方法在完整代码示例里
      
      /**
       * 处理可迭代对象的状态兑现, 只要有一个元素的状态未已解决, 那么就立即返回改元素的已解决的 `ToyPromise` 对象,
       * 如果所有元素的对象状态都为 `rejected`, 那么返回一个已拒绝的 `ToyPromise` 对象
       *
       * @param values 可迭代对象
       * @returns 首个状态兑现为 `fulfilled` 的 `ToyPromise` 对象, 全部为失败时返回标识失败的拒绝 `ToyPromise` 对象
       */
      public static any<T>(values: Iterable<T | PromiseLike<T>>): ToyPromise<ToyPromiseAwaited<T>>;
      /**
       * 处理数组内对象的状态兑现, 只要有一个元素的状态未已解决, 那么就立即返回改元素的已解决的 `ToyPromise` 对象,
       * 如果所有元素的对象状态都为 `rejected`, 那么返回一个已拒绝的 `ToyPromise` 对象
       *
       * @param values 数组
       * @returns 首个状态兑现为 `fulfilled` 的 `ToyPromise` 对象, 全部为失败时返回标识失败的拒绝 `ToyPromise` 对象
      */
      public static any<T extends readonly unknown[] | []>(values: T): ToyPromise<ToyPromiseAwaited<T[number]>>;
      public static any<T>(values: any) {
        return new ToyPromise((resolve, reject) => {
          if (!this.#isIterable(values)) {
            throw new TypeError(
              `${typeof values} ${values} is not iterable (cannot read property Symbol(Symbol.iterator))`
            );
          } else {
            let rejectedCount: number = 0;
            if (Array.isArray(values)) {
              // 如果是空数组, 那么直接抛错
              // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/any
              if (values.length === 0) {
                throw new AggregateError([], "All promises were rejected");
              }
    
              const len0: number = values.length;
              for (let i: number = 0; i < len0; ++i) {
                const current: any = values[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then(resolve as any, () => {
                    if (++rejectedCount === len0) {
                      reject(new AggregateError([], "All promises were rejected"));
                    }
                  });
                } else {
                  resolve(current as any);
                }
              }
            } else {
              const tmpList: Array<any> = [];
              for (const val of values) {
                tmpList.push(val);
              }
              // 如果可迭代遍历累计次数为 0, 那么同样直接抛错
              if (tmpList.length == 0) {
                throw new AggregateError([], "All promises were rejected");
              }
              const len1: number = tmpList.length;
              for (let i: number = 0; i < len1; ++i) {
                const current: any = tmpList[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then(resolve as any, () => {
                    if (++rejectedCount === len1) {
                      reject(new AggregateError([], "All promises were rejected"));
                    }
                  });
                } else {
                  resolve(current as any);
                }
              }
            }
          }
        });
      }
    }
    

    是时候写个简单 demo 了

    ToyPromise.all([
      1,
      2,
      new Promise<number>((resolve) => setTimeout(resolve, 1000, 3)),
      new ToyPromise<number>((resolve) => setTimeout(resolve, 600, 4)),
      ToyPromise.resolve(5)
    ])
      .then((values: Array<number>) => {
        console.log(values);
        return ToyPromise.reject("why");
      })
      .then()
      .catch()
      .catch((reason) => {
        console.warn(reason);
      })
      .then()
      .finally(() => {
        console.log("done1");
      })
      .then()
      .finally(() => {
        console.log("done2");
      });
    

    浏览器输出 image.png

添加未捕获错误控制台输出提醒

  • 目前为止看上去好像还可以, 但有时我们会注意到原生实现在拒绝后如果没有错误捕获, 处理, 浏览器会给一个控制台窗口 error 提示(提示一次), 如这样 image.png

  • 我们这里作一些简单处理, 以此达到类似效果, 我们需要一个记录保存是否有未捕获的拒绝处理, 且确保只输出一次, 那么可以这么修改(如图 \to​​ 节省篇幅, 这里只截取 code 截屏) image.png image.png image.png image.png image.png

    Promise.reject("why")
      .then(() => {
        throw new Error("1");
      })
      .catch(() => {
        throw new Error("2");
      })
      .finally()
      .then(() => {
        return new Promise((_, reject) => {
          reject("3");
        });
      })
      .catch();
    
    ToyPromise.reject("why")
      .then(() => {
        throw new Error("1");
      })
      .catch(() => {
        throw new Error("2");
      })
      .finally()
      .then(() => {
        return new Promise((_, reject) => {
          reject("3");
        });
      })
      .catch();
    
    Promise.reject("nothing").finally().catch(console.warn);
    ToyPromise.reject("nothing").finally().catch(console.warn);
    

    image.png

原生.all, .any ...可能的一些预期外行为,

  • 这个问题是我在实现 .all 等方法时想到的, 如果传入的 values 是一个数组的代理对象, 且如果恰好 get 里有些额外行为, 会如何? 直接写一个简单示例

    const baseArr: Array<number> = [1, 2, 3];
    const proxyArr = new Proxy(baseArr, {
      get(target, p, receiver) {
        console.log("tricker --1");
        return Reflect.get(target, p, receiver);
      }
    });
    
    function* gen() {
      for (let i: number = 1; i <= 3; ++i) {
        console.log("tricker --2");
    
        yield i;
      }
    }
    // 可能出现一些非预期内的行为, 但这里不作为探讨
    Promise.all(proxyArr);
    Promise.all(gen());
    

    image.png 不妨简单粗暴猜测下~~(过于直球)~~对于数组处理内部直接遍历了 12 次 + 2 次读取数组长度`(PS: z 这里存粹的主观猜测, 错了轻喷 🤣), 而对于非数组的可迭代对象, 则可能采用收集到另外一个容器的形式再次读取判断的方式(我自己写的实现实际上也是这么做) 😶‍🌫️

最后, 添加 abort(): boolean 以及完整的实现(又臭又长)

  • 记得开头枚举定义的 ABORTED 吗, 这里添加一个 abort(): boolean 用于取消一个状态为 pending 的自定义 Promise 实例(同样不可逆), 且返回一个布尔值用来判断是否唉状态落地前取消了任务

  • PS: 已存在不足: 对比原生有些地方肯定没处理好, 比如幂等, 以及 resolve 一个 Thenable 对象时的行为(其实如果 node 的 Promise 去跑那个测试会有几个测试条目过不了)..., 剩下的问题后面再改...

    /**
     * 自定义 ToyPromise 状态
     */
    enum ToyPromiseStatus {
      PENDING = "pending",
      FULFILLED = "fulfilled",
      REJECTED = "rejected",
      ABORTED = "aborted"
    }
    
    type ToyPromiseAwaited<T> = T extends null | undefined
      ? T
      : T extends object & { then(onfulfilled: infer F, ...args: infer _): any }
      ? F extends (value: infer V, ...args: infer _) => any
        ? ToyPromiseAwaited<V>
        : never
      : T;
    
    /**
     * PromiseLike 定义(摘自 ts 默认内置定义)
     */
    interface Thenable<T> {
      then<TResult1 = T, TResult2 = never>(
        onFulfilled?: ((value: T) => TResult1 | Thenable<TResult1>) | undefined | null,
        onRejected?: ((reason: any) => TResult2 | Thenable<TResult2>) | undefined | null
      ): Thenable<TResult1 | TResult2>;
    }
    
    /**
     * 执行器函数类型
     */
    type ExecutorType<T> = (resolve: (value: T) => void, reject: (reason: any) => void) => void;
    
    /**
     * 所有自定义 Promise 状态确定所返回的值
     */
    type ToyPromiseAllSettledResult<T> =
      | { status: "fulfilled"; value: T }
      | {
          status: "rejected";
          reason: any;
        };
    
    /**
     * 自定义 Promise A+ 规范简单实现
     *
     * @implements Thenable<T> 实现了 Thenable 接口并且继承其类型
     * @template T 数据类型
     * @author Shalling
     * @version 0.01
     */
    class ToyPromise<T> implements Thenable<T> {
      /**
       * 自定义 Promise 的状态
       */
      #state: ToyPromiseStatus;
    
      /**
       * 自定义 Promise 已解决时所包含的值
       */
      #value!: T;
    
      /**
       * 自定义 Promise 处于拒绝时包含的值
       */
      #reason: any;
    
      /**
       * 已解决回调函数队列
       */
      readonly #resolvedCallbacks: Array<(value: T) => void>;
    
      /**
       * 已拒绝回调函数队列
       */
      readonly #rejectedCallbacks: Array<(reason: any) => void>;
    
      /**
       * 记录处于已拒绝状态时, 是否提供了异常处理函数
       */
      #hasRejectedHandler: boolean;
    
      /**
       * 实例初始化构造器
       *
       * @param executor 状态确定执行器函数
       */
      public constructor(executor: ExecutorType<T>) {
        if (typeof executor !== "function") {
          throw new TypeError(`ToyPromise resolver #<${typeof executor}> is not a function`);
        }
    
        this.#hasRejectedHandler = false;
        this.#state = ToyPromiseStatus.PENDING;
        this.#resolvedCallbacks = [];
        this.#rejectedCallbacks = [];
    
        try {
          executor(this.#resolve.bind(this), this.#reject.bind(this));
        } catch (reason: any) {
          this.#reject(reason);
        }
      }
    
      /**
       * 提供给状态执行器函数的 `已解决` 函数
       *
       * @param value `已解决` 值
       * @template T 默认数据类型
       */
      #resolve(value: T): void {
        if (<ToyPromise<T>>value === this) {
          this.#reject(new TypeError("Chaining cycle detected for promise #<OptionalPromise>"));
          return;
        }
        if (value instanceof Promise || value instanceof ToyPromise) {
          (<Thenable<T>>value).then(this.#resolve.bind(this), this.#reject.bind(this));
          return;
        }
        // TODO: WAIT RESOLVE
        // if (ToyPromise.#isThenable(value)) {
        //   ToyPromise.#processThenableOrNor(this, value, this.#resolve.bind(this), this.#reject.bind(this));
        //   return;
        // }
        if (this.#state === ToyPromiseStatus.PENDING) {
          this.#value = value;
          this.#state = ToyPromiseStatus.FULFILLED;
          for (const cb of this.#resolvedCallbacks) {
            cb(this.#value);
          }
        }
      }
    
      /**
       * 提供给状态执行器函数的 `已拒绝` 函数
       *
       * @param reason `已拒绝` 原因
       * @private
       */
      #reject(reason: any): void {
        if (this.#state === ToyPromiseStatus.PENDING) {
          this.#reason = reason;
          this.#state = ToyPromiseStatus.REJECTED;
          for (const cb of this.#rejectedCallbacks) {
            cb(this.#reason);
          }
          queueMicrotask(() => {
            if (!this.#hasRejectedHandler) {
              console.error(`Uncaught (in toy-promise)📌`, reason);
            }
          });
        }
      }
    
      /**
       * 通过递归方式处理可能为 `Thenable` 结构的对象
       *
       * @param promise 自定义 Promise 实例
       * @param value 进行判断处理的值
       * @param resolve 已解决函数
       * @param reject 已拒绝函数
       * @template T 默认数据类型
       */
      static #processThenableOrNor<T>(
        promise: ToyPromise<T>,
        value: any,
        resolve: (value: T) => void,
        reject: (reason: any) => void
      ) {
        if (promise === value) {
          return reject(new TypeError(`Chaining cycle detected for promise #<OptionalPromise>`));
        }
        let isCall: boolean = false;
        if ((value && typeof value === "object") || typeof value === "function") {
          /*
            使用 try-catch 避免如下情况
            Object.defineProperty(obj, "then", {
              get() {
                throw new Error("msg xxx")
              }
            })
          */
          try {
            const thenFunctor: ((resolve: (value: T) => void, reject: (reason: any) => void) => any) | undefined | null =
              value["then"];
            if (typeof thenFunctor === "function") {
              thenFunctor.call(
                value,
                (y) => {
                  if (!isCall) {
                    isCall = true;
                    this.#processThenableOrNor(value, y, resolve, reject);
                  }
                },
                (r) => {
                  if (!isCall) {
                    isCall = true;
                    reject(r);
                  }
                }
              );
            } else {
              resolve!(value);
            }
          } catch (reason) {
            if (!isCall) {
              isCall = true;
              reject(reason);
            }
          }
        } else {
          resolve!(value);
        }
      }
    
      /**
       * 返回链式生成的自定义 promise 实例
       *
       * @param onFulfilled `已解决` 状态捕获函数
       * @param onRejected `已拒绝/错误` 状态捕获调函数
       * @template T 接收的已解决类型
       * @template R 接收的以决绝类型
       * @return ToyPromise 一个新的自定义 promise 实例
       */
      public then<E = T, R = never>(
        onFulfilled?: ((value: T) => E | Thenable<E>) | undefined | null,
        onRejected?: ((reason: any) => R | Thenable<R>) | undefined | null
      ): ToyPromise<E | R> {
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val as Thenable<E>;
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : (reason) => {
                throw reason;
              };
        const retPromise = new ToyPromise<E | R>((resolve, reject) => {
          switch (this.#state) {
            case ToyPromiseStatus.FULFILLED:
              queueMicrotask(() => {
                try {
                  ToyPromise.#processThenableOrNor(retPromise, onFulfilled!(this.#value), resolve, reject);
                } catch (reason) {
                  reject(reason);
                }
              });
              break;
            case ToyPromiseStatus.REJECTED:
              queueMicrotask((): void => {
                try {
                  ToyPromise.#processThenableOrNor(retPromise, onRejected!(this.#reason), resolve, reject);
                } catch (reason) {
                  reject(reason);
                }
              });
              break;
            /*
              如果是 pending 状态(如 executor 在 setTimeout 中执行), 则将包装函数推入队列,
              等 executor 到达执行状态, 内部的 #resolve(val), #reject(reason) 各自遍历 `pending` 状态时推入队列里的包装函数
            */
            case ToyPromiseStatus.PENDING:
              this.#resolvedCallbacks.push((value: T): void =>
                queueMicrotask((): void => {
                  try {
                    ToyPromise.#processThenableOrNor(retPromise, onFulfilled!(value), resolve, reject);
                  } catch (reason) {
                    reject(reason);
                  }
                })
              );
              this.#rejectedCallbacks.push((reason: any): void =>
                queueMicrotask((): void => {
                  try {
                    ToyPromise.#processThenableOrNor(retPromise, onRejected!(reason), resolve, reject);
                  } catch (reason) {
                    reject(reason);
                  }
                })
              );
              break;
            default:
              break;
          }
        });
    
        this.#hasRejectedHandler = true;
        return retPromise;
      }
    
      /**
       * `错误/已拒绝` 状态处理方法
       *
       * @param onRejected `已拒绝/错误` 状态捕获调函数
       * @define 这个方法为 `then` 方法的签名
       * @return ToyPromise 一个新的自定义 promise 实例
       */
      public catch<TResult = never>(
        onRejected?: ((reason: any) => TResult | Thenable<TResult>) | undefined | null
      ): ToyPromise<T | TResult> {
        return this.then(null, onRejected) as any;
      }
    
      /**
       * 最终执行函数(这个方为 `then` 方法的签名版本)
       *
       * @param onFinally 回调执行函数
       * @return 一个新的自定义 promise 实例(会保持之前的 `已解决/已拒绝` 状态
       */
      public finally(onFinally?: (() => void) | undefined | null): ToyPromise<T> {
        // 提前处理 ToyPromise.ts:152 Uncaught (in toy-promise)📌 TypeError: onFinally is not a function 这种可能的情况
        onFinally = typeof onFinally === "function" ? onFinally : () => {};
    
        return this.then(
          (value) => ToyPromise.resolve(onFinally!()).then(() => value),
          (reason) =>
            ToyPromise.resolve(onFinally!()).then(() => {
              throw reason;
            })
        );
      }
    
      /**
       * 判断一个对象是否为 `thenable` 对象
       *
       * @param obj 需要进行判断的对象
       * @return 对象是否为 thenable 对象
       */
      static #isThenable(obj: any): boolean {
        if ((obj && typeof obj === "object") || typeof obj === "function") {
          return typeof obj["then"] === "function";
        }
        return false;
      }
    
      /**
       * 判断一个对象是否为可迭代对象
       *
       * @param obj 需要进行判断的对象
       * @return 对象是否为可迭代对象
       */
      static #isIterable(obj: any): boolean {
        // 避免 "" 空字符(那么是 iterable) 被判断为 false 条件
        if (typeof obj === "string") {
          return true;
        }
        // 如果是 null 直接返回 false
        if (typeof obj === "object" && !obj) {
          return false;
        }
        // 判断是否为可迭代对象
        if (typeof obj === "function" || typeof obj === "object") {
          /*
            兼容如下这种示例情况
            function foo() {}
            foo[Symbol.iterator] = () => { yield 233 }
          */
          return typeof obj[Symbol.iterator] === "function";
        } else {
          return false;
        }
      }
    
      /**
       * 根据是否在状态是否已兑现前打断操作返回处理的结果
       * 如果当前状态为未兑现(即为: pending), 那么将其操作提前放弃
       * 否则如果状态已经处于确定状态: `fulfilled/rejected`, 那么不做任何修改
       *
       * @returns 如果在状态未兑现前结束操作返回 {@code true}, 否则返回 {@code false}
       */
      public abort(): boolean {
        if (this.#state === ToyPromiseStatus.PENDING) {
          this.#state = ToyPromiseStatus.ABORTED;
          this.#reason = "aborted";
          return true;
        } else {
          return false;
        }
      }
    
      /**
       * @param values 可迭代对象
       * @returns 如果可迭代对象内的所有元素的兑现状态为已解决, 那么返回包含可迭代对象遍历的所有元所组成的已解决 `ToyPromise` 对象, 否则返回包含已拒绝的 `ToyPromise` 对象
       */
      public static all<T>(values: Iterable<T | PromiseLike<T>>): ToyPromise<ToyPromiseAwaited<T>[]>;
      /**
       * @param values 数组
       * @returns 如果数组内的所有元素的兑现状态为已解决, 那么返回包含可迭代对象遍历的所有元所组成的已解决 `ToyPromise` 对象, 否则返回包含已拒绝的 `ToyPromise` 对象
       */
      public static all<T extends readonly unknown[] | []>(
        values: T
      ): ToyPromise<{ -readonly [P in keyof T]: ToyPromiseAwaited<T[P]> }>;
      public static all<T>(values: any) {
        return new ToyPromise<any>((resolve, reject) => {
          // 如果接收值不可迭代, 直接返回一个包含错误提示的 ToyPromise
          if (!this.#isIterable(values)) {
            throw new TypeError(
              `${typeof values} ${values} is not iterable (cannot read property Symbol(Symbol.iterator))`
            );
          } else {
            // 已解决统计
            let resolvedCount: number = 0;
            // 存放已解决值的数组
            const resolvedList: Array<any> = [];
            const processStatus = (idx: number, data: any, countLen: number) => {
              resolvedList[idx] = data;
              if (++resolvedCount === countLen) {
                resolve(resolvedList as any);
              }
            };
            // 如果是正常 Array, 直接遍历
            if (Array.isArray(values)) {
              const len0: number = values.length;
              loop: for (let i: number = 0; i < len0; ++i) {
                const current: any = values[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then((value) => {
                    processStatus(i, value, len0);
                  }, reject);
                } else {
                  processStatus(i, current, len0);
                }
              }
            } else {
              const tmpList: Array<any> = [];
              for (const val of values) {
                tmpList.push(val);
              }
              const len1: number = tmpList.length;
              for (let i: number = 0; i < len1; ++i) {
                const current = tmpList[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then((value) => {
                    processStatus(i, value, len1);
                  }, reject);
                } else {
                  processStatus(i, current, len1);
                }
              }
            }
          }
        });
      }
    
      /**
       * 等待所有可迭代对象内所有元素状态已兑现(fulfilled 或者 rejected), 返回处理结果
       *
       * @param values 可迭代对象
       * @returns 可跌打兑现内所有元素兑现状态所组成的数组(会维持可迭代兑现元素遍历顺序)
       */
      public static allSettled<T>(
        values: Iterable<T | Thenable<T>>
      ): ToyPromise<ToyPromiseAllSettledResult<ToyPromiseAwaited<T>>[]>;
      /**
       * 等待所有可迭代对象状态已兑现(fulfilled 或者 rejected), 返回处理结果
       *
       * @param values 一个数组对象(元素可以包含 Thenable 对象)
       * @returns 所有元素兑现状态所组成的数组(会维持原有数组的元素位置)
       */
      public static allSettled<T extends readonly unknown[] | []>(
        values: T
      ): ToyPromise<{
        -readonly [P in keyof T]: ToyPromiseAllSettledResult<ToyPromiseAwaited<T[P]>>;
      }>;
      public static allSettled<T>(values: any) {
        return new ToyPromise<any>((resolve) => {
          if (!this.#isIterable(values)) {
            if (!this.#isIterable(values)) {
              throw new TypeError(
                `${typeof values} ${values} is not iterable (cannot read property Symbol(Symbol.iterator))`
              );
            }
          } else {
            let doneCount: number = 0;
            const doneList: Array<any> = [];
    
            const processStatus = (
              index: number,
              data: any,
              tType: ToyPromiseStatus.FULFILLED | ToyPromiseStatus.REJECTED = ToyPromiseStatus.FULFILLED,
              length: number
            ): void => {
              doneList[index] = {
                status: tType === ToyPromiseStatus.FULFILLED ? tType : ToyPromiseStatus.REJECTED,
                [tType === ToyPromiseStatus.FULFILLED ? "value" : "reason"]: data
              };
              if (++doneCount === length) {
                resolve(doneList as any);
              }
            };
            // 如果为数组那么直接遍历
            if (Array.isArray(values)) {
              const listLen: number = values.length;
              for (let i: number = 0; i < listLen; ++i) {
                const current: any = values[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then(
                    (value) => {
                      processStatus(i, value, ToyPromiseStatus.FULFILLED, listLen);
                    },
                    (reason) => {
                      processStatus(i, reason, ToyPromiseStatus.REJECTED, listLen);
                    }
                  );
                } else {
                  processStatus(i, current, ToyPromiseStatus.FULFILLED, listLen);
                }
              }
            }
            // 处理非数组但可迭代的数据
            else {
              const tmpList: Array<any> = [];
              for (const val of values) {
                tmpList.push(val);
              }
              const tmpListLen: number = tmpList.length;
              for (let i: number = 0; i < tmpListLen; ++i) {
                const current = tmpList[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then(
                    (value) => {
                      processStatus(i, value, ToyPromiseStatus.FULFILLED, tmpListLen);
                    },
                    (reason) => {
                      processStatus(i, reason, ToyPromiseStatus.REJECTED, tmpListLen);
                    }
                  );
                } else {
                  processStatus(i, current, ToyPromiseStatus.FULFILLED, tmpListLen);
                }
              }
            }
          }
        });
      }
    
      /**
       * 处理可迭代对象的状态兑现, 只要有一个元素的状态未已解决, 那么就立即返回改元素的已解决的 `ToyPromise` 对象,
       * 如果所有元素的对象状态都为 `rejected`, 那么返回一个已拒绝的 `ToyPromise` 对象
       *
       * @param values 可迭代对象
       * @returns 首个状态兑现为 `fulfilled` 的 `ToyPromise` 对象, 全部为失败时返回标识失败的拒绝 `ToyPromise` 对象
       */
      public static any<T>(values: Iterable<T | PromiseLike<T>>): ToyPromise<ToyPromiseAwaited<T>>;
      /**
       * 处理数组内对象的状态兑现, 只要有一个元素的状态未已解决, 那么就立即返回改元素的已解决的 `ToyPromise` 对象,
       * 如果所有元素的对象状态都为 `rejected`, 那么返回一个已拒绝的 `ToyPromise` 对象
       *
       * @param values 数组
       * @returns 首个状态兑现为 `fulfilled` 的 `ToyPromise` 对象, 全部为失败时返回标识失败的拒绝 `ToyPromise` 对象
       */
      public static any<T extends readonly unknown[] | []>(values: T): ToyPromise<ToyPromiseAwaited<T[number]>>;
      public static any<T>(values: any) {
        return new ToyPromise((resolve, reject) => {
          if (!this.#isIterable(values)) {
            throw new TypeError(
              `${typeof values} ${values} is not iterable (cannot read property Symbol(Symbol.iterator))`
            );
          } else {
            let rejectedCount: number = 0;
            if (Array.isArray(values)) {
              // 如果是空数组, 那么直接抛错
              // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/any
              if (values.length === 0) {
                throw new AggregateError([], "All promises were rejected");
              }
    
              const len0: number = values.length;
              for (let i: number = 0; i < len0; ++i) {
                const current: any = values[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then(resolve as any, () => {
                    if (++rejectedCount === len0) {
                      reject(new AggregateError([], "All promises were rejected"));
                    }
                  });
                } else {
                  resolve(current as any);
                }
              }
            } else {
              const tmpList: Array<any> = [];
              for (const val of values) {
                tmpList.push(val);
              }
              // 如果可迭代遍历累计次数为 0, 那么同样直接抛错
              if (tmpList.length == 0) {
                throw new AggregateError([], "All promises were rejected");
              }
              const len1: number = tmpList.length;
              for (let i: number = 0; i < len1; ++i) {
                const current: any = tmpList[i];
                if (this.#isThenable(current)) {
                  (<Thenable<T>>current).then(resolve as any, () => {
                    if (++rejectedCount === len1) {
                      reject(new AggregateError([], "All promises were rejected"));
                    }
                  });
                } else {
                  resolve(current as any);
                }
              }
            }
          }
        });
      }
    
      /**
       * 处理可迭代对象内第一个元素状态兑现的结果, 根据落地状态立即返回其结果
       *
       * @param values 可迭代对象
       * @returns 首个状态已兑现 的 `ToyPromise` 对象
       */
      public static race<T>(values: Iterable<T | Thenable<T>>): ToyPromise<ToyPromiseAwaited<T>>;
      /**
       * 处理可迭代对象内第一个元素状态兑现的结果, 根据落地状态立即返回其结果
       *
       * @param values 数组对象
       * @returns 首个状态已兑现 的 `ToyPromise` 对象
       */
      public static race<T extends readonly unknown[] | []>(values: T): ToyPromise<ToyPromiseAwaited<T[number]>>;
      public static race<T>(values: any) {
        return new ToyPromise((resolve, reject) => {
          for (const val of values) {
            if (this.#isThenable(val)) {
              (<Thenable<T>>val).then(resolve as any, reject);
            } else {
              resolve(val as any);
            }
          }
        });
      }
    
      /**
       * @returns 返回一个状态为已解决的 ToyPromise<void> 对象
       */
      public static resolve(): ToyPromise<void>;
      /**
       * 如果传入对象为一个 期约对象, 那么返回的对象为幂等, 即: a: Thenable; c = static.resolve(a), a === c
       *
       * @param value 期待值
       * @returns 返回一个状态为已解决的 ToyPromise<T> 的对象, 即使传入的对象为一个未状态拒绝的 Thenable 对象
       */
      public static resolve<T>(value: T): ToyPromise<ToyPromiseAwaited<T>>;
      /**
       * 如果传入对象为一个 期约对象, 那么返回的对象为幂等, 即: a: Thenable; c = static.resolve(a), a === c
       *
       * @param value 期待值
       * @returns 返回一个状态为已解决的 ToyPromise<T> 的对象, 即使传入的对象为一个未状态拒绝的 Thenable 对象
       */
      public static resolve<T>(value: T | Thenable<T>): ToyPromise<ToyPromiseAwaited<T>>;
      public static resolve(value?: any) {
        // 接口幂等, 兼容原生 Promise 和自定义 Promise
        /*
          const p1 = new ToyPromise(()=> {});
          const p2 = ToyPromise.resolve(p1);
          console.log(p1 === p2); // true
        */
    
        /*
         const p1 = new ToyPromise((_ignored, reject) => {
          reject(111);
        });
          const p2 = ToyPromise.resolve(p1);
          console.log(p1 === p2); // true
        */
        if (value instanceof ToyPromise || value instanceof Promise) {
          return value;
        }
        return new ToyPromise((resolve) => {
          resolve(value);
        });
      }
      /**
       * @param reason 决绝原因/值
       * @returns 返回一个状态为已拒绝的 ToyPromise<T> 的对象
       */
    
      public static reject<T = never>(reason?: any): ToyPromise<T> {
        return new ToyPromise<T>((_ignore, reject) => {
          reject(reason);
        });
      }
    
      /**
       * 返回一个对象, 其包含一个新的 ToyPromise 对象和两个函数, 用于解决或拒绝它, 对应于传入给 ToyPromise() 构造函数执行器的两个参数
       * @see https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
       *
       * @returns 包含一个新的 ToyPromise 实例对象和两个函数, 用于解决或拒绝它
       */
      public static withResolvers<T = unknown>(): {
        promise: ToyPromise<unknown>;
        resolve: (val: T) => void;
        reject: (reason: any) => void;
      } {
        let res: (val: T) => void;
        let rej: (reason: any) => void;
        const promise = new ToyPromise((resolve, reject) => {
          res = resolve;
          rej = reject;
        });
    
        return {
          promise,
          resolve: res!,
          reject: rej!
        };
      }
    
      public get [Symbol.toStringTag](): string {
        return "ToyPromise";
      }
    }
    
    export { ToyPromise };
    
  • 以及一个简单的测试

    const tp = new ToyPromise<number>((resolve) => {
      setTimeout(resolve, 1000, 4);
    })
      .then((value) => {
        console.log(value);
        return ToyPromise.all([...Array.from(gen(3)), value]);
      })
      .then((values: Array<number>) => {
        console.log(values);
        return values.reduce((prev, current) => prev + current, 0);
      })
      .then(console.log, () => {
        throw "";
      })
      .then(() => {
        throw "hint";
      })
      .catch(console.warn)
      .finally(() => {
        console.log("apple seed");
      });
    
    setTimeout(() => {
      const isPrevBrokeSuccess = tp.abort();
      isPrevBrokeSuccess ? console.log(" ---> 提前终止") : console.warn("状态已定");
    }, 1000);
    
    tp.then(() => {
      return new ToyPromise((resolve) => setTimeout(resolve, 1800, "A"));
    }).finally(() => {
      console.log("never got it!");
    });
    

    image.png