手写Promise: 实现原生Promise的行为和功能

78 阅读7分钟

描述

MyPromise 实现了一个基本的 Promise 功能,包括状态管理、回调处理和静态方法。通过微任务处理,确保异步任务的正确执行顺序,适用于大多数现代浏览器环境。

知识点

  • 使用 queueMicrotaskMutationObserversetTimeout 确保回调函数在微任务队列中执行。
  /**
   * 用于在微任务队列中执行回调函数。
   * @param callback 
   */
  const runQueueMicrotask = (callback) => {
    if (typeof queueMicrotask === "function") {
      queueMicrotask(callback);
    } else if (typeof MutationObserver === "function") {
      const observer = new MutationObserver(callback);
      const node = document.createTextNode("");
      observer.observe(node, { characterData: true });
      node.data = "";
    } else {
      setTimeout(callback);
    }
  };

一、实现构造函数

  • 接受一个执行器函数 executor,该函数接受两个参数:resolvereject
  • resolve 函数用于将 Promise 状态从 PENDDING 变为 FULFILLED,并触发回调。
  • reject 函数用于将 Promise 状态从 PENDDING 变为 REJECTED,并触发回调。
/**
*

// 状态常量
const PENDDING = "pendding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
class MyPromise {
  // 状态
  #state = PENDDING;
  constructor(executor) {
    // 成功回调 pendding => fulfilled
    const resolve = (value) => {};
    // 失败回调 pendding => rejected
    const reject = (reason) => {};
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
}

二、 实现then方法

  • 接受两个参数:onFulfilledonRejected,分别处理成功和失败的情况。
  • 返回一个新的 Promise,确保链式调用。
  • 根据当前状态,立即执行回调或存储回调等待状态改变。
/**
 * @des 实现功能
 *
 * 1.状态不可变性 
 *    pendding => fulfilled(实现)
 *    pendding => rejected(拒绝)
 * 2.异步及多次调用
 * 3.返回值是promise以及链式调用
 * 4. 重复引用抛出异常 Chaining cycle detected for promise #<Promise>
 */
class MyPromise {
  constructor(executor) {
    // ···
  }

  // then 方法
  then(onFulfilled, onRejected) {
    // 规范要求,如果 onFulfilled 或 onRejected 不是函数,则忽略该参数,直接返回原值
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (x) => {
            throw x;
          };
    // 返回一个新的 promise
    const p2 = new MyPromise((resolve, reject) => {
      const handlerCallback = (callback) => {
        return () => {
          runQueueMicrotask(() => {
            try {
              const x = callback(this.#result);
              // 如果 x 与 p2 相等,则抛出异常
              if (x === p2) {
                throw new TypeError(
                  "Chaining cycle detected for promise #<Promise>"
                );
              }
              if (x instanceof MyPromise) {
                // 如果 x 是一个 promise,则将其状态一路向下传递,直到其状态为 fulfilled 或 rejected
                x.then(
                  (res) => resolve(res),
                  (err) => reject(err)
                );
              } else {
                // 如果 x 是一个对象,则将其直接返回
                resolve(x);
              }
            } catch (e) {
              // 捕获异常,如果**特别**的异常,则抛出异常,否则将异常传递给 reject
              if (
                e.message === "Chaining cycle detected for promise #<Promise>"
              ) {
                throw e.message;
              }
              reject(e);
            }
          });
        };
      };
      // 这里会有三种情况:
      if (this.#state === FULFILLED) {
        // 1. 当前 promise 状态为 fulfilled,则立即执行 onFulfilled 并返回一个新的 promise
        handlerCallback(onFulfilled)();
      } else if (this.#state === REJECTED) {
        // 2. 当前 promise 状态为 rejected,则立即执行 onRejected 并返回一个新的 promise
        handlerCallback(onRejected)();
      } else {
        // 3. 当前 promise 状态为 pendding,则将 onFulfilled 和 onRejected 存入 handlers 数组,等待状态改变时执行
        this.#handlers.push({
          onFulfilled: handlerCallback(onFulfilled),
          onRejected: handlerCallback(onRejected),
        });
      }
    });
    return p2;
  }
}

三、实例方法: catch/finally

  • 接受一个 onRejected 回调函数,等同于调用 then(undefined, onRejected)
  • 接受一个 onFinally 回调函数,无论 Promise 最终状态如何都会执行。
class MyPromise {
  // catch方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  // finally方法
  finally(onFinally) {
    return this.then(onFinally, onFinally);
  }
}

四、静态方法: resolve/reject

  • 返回一个已解决的 Promise。
  • 返回一个已拒绝的 Promise。
class MyPromise {
  // resolve静态方法
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  // reject静态方法
  static reject(value) {
    return new MyPromise((undefined, reject) => {
      reject(value);
    });
  }
}

五、静态方法: race

  • 接受一个 Promise 数组,返回一个 Promise,其状态由数组中最先改变状态的 Promise 决定。
class MyPromise {
  // race静态方法
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      promises.forEach((p) => {
        MyPromise.resolve(p).then(
          (res) => {
            resolve(res);
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }
}


六、静态方法: all

  • 接受一个 Promise 数组,返回一个 Promise,当所有 Promise 都成功时才成功,否则失败。
class MyPromise {
  // all静态方法
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      // 检查参数是否为数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 处理空数组的情况
      if (promises.length === 0) {
        return resolve([]);
      }
      // 初始化结果数组和计数器
      const results = [];
      let count = 0;
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          (res) => {
            // 存储每个 Promise 的结果和跟踪已完成的 Promise 数量
            results[index] = res;
            if (++count === promises.length) {
              resolve(results);
            }
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }
}

七、静态方法: allSettled

  • 接受一个 Promise 数组,返回一个 Promise,当所有 Promise 都完成(无论成功或失败)时成功。
class MyPromise {
  // allSettled静态方法
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      // 检查参数是否为数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 处理空数组的情况
      if (promises.length === 0) {
        return resolve([]);
      }
      // 初始化结果数组和计数器
      const results = [];
      let count = 0;
      // handleResult 函数用于将每个 Promise 的结果存储到 results 数组中,并在所有 Promise 都处理完毕后调用 resolve。
      const handleResult = (index, result) => {
        results[index] = result;
        if (++count === promises.length) {
          resolve(results);
        }
      };
      // 遍历 promises 数组,使用 MyPromise.resolve(p) 确保每个元素都是一个 Promise。然后,为每个 Promise 注册 then 回调,分别处理成功和失败的情况,并将结果传递给 handleResult 函数。
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          (res) => handleResult(index, { status: FULFILLED, value: res }),
          (err) => handleResult(index, { status: REJECTED, reason: err })
        );
      });
    });
  }
}

八、静态方法: any

  • 接受一个 Promise 数组,返回一个 Promise,当数组中任意一个 Promise 成功时成功,所有都失败时失败。
class MyPromise {
  // any静态方法
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      // 检查参数是否为数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 处理空数组的情况
      if (promises.length === 0) {
        return reject(new AggregateError([], "All promises were rejected"));
      }

      const errors = new Array(promises.length);
      let rejectedCount = 0;

      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(resolve, (err) => {
          errors[index] = err;
          if (++rejectedCount === promises.length) {
            reject(new AggregateError(errors, "All promises were rejected"));
          }
        });
      });
    });
  }
}

完整代码

// 状态常量
const PENDDING = "pendding";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";
// 在微任务队列中执行回调函数
const runQueueMicrotask = (callback) => {
  if (typeof queueMicrotask === "function") {
    queueMicrotask(callback);
  } else if (typeof MutationObserver === "function") {
    const observer = new MutationObserver(callback);
    const node = document.createTextNode("");
    observer.observe(node, { characterData: true });
    node.data = "";
  } else {
    setTimeout(callback);
  }
};
class MyPromise {
  // 状态
  #state = PENDDING;
  #result = undefined;
  #handlers = [];
  constructor(executor) {
    // 成功回调 pendding => fulfilled
    const resolve = (value) => {
      if (this.#state === PENDDING) {
        this.#state = FULFILLED;
        this.#result = value;
        this.#handlers.forEach(({ onFulfilled }) => {
          onFulfilled(this.#result);
        });
      }
    };
    // 失败回调 pendding => rejected
    const reject = (reason) => {
      if (this.#state === PENDDING) {
        this.#state = REJECTED;
        this.#result = reason;
        this.#handlers.forEach(({ onRejected }) => {
          onRejected(this.#result);
        });
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  // then 方法
  then(onFulfilled, onRejected) {
    // 规范要求,如果 onFulfilled 或 onRejected 不是函数,则忽略该参数,直接返回原值
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (x) => x;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (x) => {
            throw x;
          };
    // 返回一个新的 promise
    const p2 = new MyPromise((resolve, reject) => {
      const handlerCallback = (callback) => {
        return () => {
          runQueueMicrotask(() => {
            try {
              const x = callback(this.#result);
              // 如果 x 与 p2 相等,则抛出异常
              if (x === p2) {
                throw new TypeError(
                  "Chaining cycle detected for promise #<Promise>"
                );
              }
              if (x instanceof MyPromise) {
                // 如果 x 是一个 promise,则将其状态一路向下传递,直到其状态为 fulfilled 或 rejected
                x.then(
                  (res) => resolve(res),
                  (err) => reject(err)
                );
              } else {
                // 如果 x 是一个对象,则将其直接返回
                resolve(x);
              }
            } catch (e) {
              // 捕获异常,如果**特别**的异常,则抛出异常,否则将异常传递给 reject
              if (
                e.message === "Chaining cycle detected for promise #<Promise>"
              ) {
                throw e.message;
              }
              reject(e);
            }
          });
        };
      };
      // 这里会有三种情况:
      if (this.#state === FULFILLED) {
        // 1. 当前 promise 状态为 fulfilled,则立即执行 onFulfilled 并返回一个新的 promise
        handlerCallback(onFulfilled)();
      } else if (this.#state === REJECTED) {
        // 2. 当前 promise 状态为 rejected,则立即执行 onRejected 并返回一个新的 promise
        handlerCallback(onRejected)();
      } else {
        // 3. 当前 promise 状态为 pendding,则将 onFulfilled 和 onRejected 存入 handlers 数组,等待状态改变时执行
        this.#handlers.push({
          onFulfilled: handlerCallback(onFulfilled),
          onRejected: handlerCallback(onRejected),
        });
      }
    });
    return p2;
  }

  // catch方法
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  // finally方法
  finally(onFinally) {
    return this.then(onFinally, onFinally);
  }

  // resolve静态方法
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => {
      resolve(value);
    });
  }

  // reject静态方法
  static reject(value) {
    return new MyPromise((undefined, reject) => {
      reject(value);
    });
  }
  // race静态方法
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      promises.forEach((p) => {
        MyPromise.resolve(p).then(resolve, reject);
      });
    });
  }

  // all静态方法
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      // 检查参数是否为数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 处理空数组的情况
      if (promises.length === 0) {
        return resolve([]);
      }
      // 初始化结果数组和计数器
      const results = [];
      let count = 0;
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          (res) => {
            // 存储每个 Promise 的结果和跟踪已完成的 Promise 数量
            results[index] = res;
            if (++count === promises.length) {
              resolve(results);
            }
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }
  // allSettled静态方法
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      // 检查参数是否为数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 处理空数组的情况
      if (promises.length === 0) {
        return resolve([]);
      }
      // 初始化结果数组和计数器
      const results = [];
      let count = 0;
      // handleResult 函数用于将每个 Promise 的结果存储到 results 数组中,并在所有 Promise 都处理完毕后调用 resolve。
      const handleResult = (index, result) => {
        results[index] = result;
        if (++count === promises.length) {
          resolve(results);
        }
      };
      // 遍历 promises 数组,使用 MyPromise.resolve(p) 确保每个元素都是一个 Promise。然后,为每个 Promise 注册 then 回调,分别处理成功和失败的情况,并将结果传递给 handleResult 函数。
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          (res) => handleResult(index, { status: FULFILLED, value: res }),
          (err) => handleResult(index, { status: REJECTED, reason: err })
        );
      });
    });
  }
  // any静态方法
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      // 检查参数是否为数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError("Argument is not iterable"));
      }
      // 处理空数组的情况
      if (promises.length === 0) {
        return reject(new AggregateError([], "All promises were rejected"));
      }

      const errors = new Array(promises.length);
      let rejectedCount = 0;

      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(resolve, (err) => {
          errors[index] = err;
          if (++rejectedCount === promises.length) {
            reject(new AggregateError(errors, "All promises were rejected"));
          }
        });
      });
    });
  }
}