Promise 实例方法catch、finally(八)

43 阅读1分钟

image.png

image.png

  <script>
    function runAsyncTask(callback) {
      if (typeof queueMicrotask === "function") {
        queueMicrotask(callback);
      } else if (typeof MutationObserver === "function") {
        const obs = new MutationObserver(callback);
        const divNode = document.createElement("div");
        obs.observe(divNode, { childList: true });
        divNode.innerText = "fx666";
      } else {
        setTimeout(callback, 0);
      }
    }

    function resolvePromise(p2, x, resolve, reject) {
      if (x === p2) {
        // console.log('返回了p2')
        // 2. 抛出错误 Chaining cycle detected for promise #<Promise>
        throw new TypeError("Chaining cycle detected for promise #<Promise>");
      }
      if (x instanceof FXPromise) {
        x.then(
          (res) => resolve(res),
          (err) => reject(err)
        );
      } else {
        resolve(x);
      }
    }

    const PENDING = "pending";
    const FULFILLED = "fulfilled";
    const REJECTED = "rejected";
    class FXPromise {
      state = PENDING;
      result = undefined;
      #handlers = [];

      constructor(fn) {
        const resolveFunc = (res) => {
          if (this.state === PENDING) {
            this.result = res;
            this.state = FULFILLED;
            this.#handlers.forEach(({ onFulfilled }) => {
              onFulfilled(this.result);
            });
          }
        };
        const rejectFunc = (res) => {
          if (this.state === PENDING) {
            this.result = res;
            this.state = REJECTED;
            this.#handlers.forEach(({ onRejected }) => onRejected(this.result));
          }
        };
        try {
          fn(resolveFunc, rejectFunc);
        } catch (error) {
          rejectFunc(error);
        }
      }

      then(onFulfilled, onRejected) {
        onFulfilled =
          typeof onFulfilled === "function" ? onFulfilled : (x) => x;
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : (x) => {
                throw x;
              };
        const p2 = new FXPromise((resolve, reject) => {
          if (this.state === FULFILLED) {
            runAsyncTask(() => {
              try {
                const x = onFulfilled(this.result);
                resolvePromise(p2, x, resolve, reject);
              } catch (error) {
                reject(error);
              }
            });
          } else if (this.state === REJECTED) {
            runAsyncTask(() => {
              try {
                const x = onRejected(this.result);
                resolvePromise(p2, x, resolve, reject);
              } catch (error) {
                reject(error);
              }
            });
          } else if (this.state === PENDING) {
            this.#handlers.push({
              onFulfilled: (res) => {
                runAsyncTask(() => {
                  try {
                    const x = onFulfilled(res);
                    resolvePromise(p2, x, resolve, reject);
                  } catch (error) {
                    reject(error);
                  }
                });
              },
              onRejected: (res) => {
                runAsyncTask(() => {
                  try {
                    const x = onRejected(res);
                    resolvePromise(p2, x, resolve, reject);
                  } catch (error) {
                    reject(error);
                  }
                });
              },
            });
          }
        });
        return p2;
      }

      /**
       * catch方法
       * 1. 内部调用then方法
       * 2. 处理异常
       * */
      catch(onRejected) {
        // 内部调用then方法
        return this.then(null, onRejected);
      }

      finally(onFinally) {
        // 内部调用then方法
        return this.then(onFinally, onFinally);
      }
    }

    const p = new FXPromise((resolve, reject) => {
      //   reject("reject-error");
      throw "throw-error";
    });

    p.then((res) => {
      console.log("res: ", res);
    })
      .catch((e) => {
        console.log("catch: ", e);
      })
      .finally(() => {
        console.log("finally");
      });
  </script>