Skip to content

MyPromise 实现

概述

本报告详细分析了一个完整的 Promise/A+ 规范实现,包括核心功能、静态方法和工具函数。该实现严格遵循 Promise/A+ 规范,并提供了与原生 Promise 兼容的 API。

Promise/A+规范:https://promisesaplus.com/

实现架构

核心组件

  • MyPromise 类:主要的 Promise 实现
  • resolvePromise 函数:Promise 解析程序,处理复杂的值解析逻辑
  • 工具函数:辅助函数,提高代码复用性和可维护性

1. 工具函数实现

1.1 错误处理工具函数

javascript
// 工具函数 减少try catch 使用
function tryCatchFn(execFn, value, resolve, reject) {
  try {
    const result = execFn(value);
    resolve(result);
  } catch (err) {
    reject(err);
  }
}

实现要点:

  • 统一处理回调函数的执行和异常捕获
  • 减少代码重复,提高可维护性
  • 确保任何回调函数抛出的异常都能被正确捕获并转换为 rejected Promise

1.2 可迭代对象检测函数

javascript
// 判断对象是否可迭代
function isIterable(obj) {
  return obj !== null && obj !== undefined && typeof obj[Symbol.iterator] === "function";
}

实现要点:

  • 检查 Symbol.iterator 方法的存在性
  • 用于静态方法中验证参数的有效性

2. Promise 解析程序

2.1 resolvePromise 函数

javascript
// Promise解析程序
function resolvePromise(promise, value, resolve, reject) {
  // 1. x 与 MyPromise 相等 防止传入自身的时候,无限循环 自引用检测
  if (value === promise) {
    return reject(new TypeError("循环引用"));
  }
  
  // 2. 如果 value 是 MyPromise 实例
  if (value instanceof MyPromise) {
    // 把当前实例的状态挂靠到 value 上
    value.then(resolve, reject);
    return;
  }
  
  // 3. 如果 value 是对象或者函数
  if (value !== null && (typeof value === "object" || typeof value === "function")) {
    let then;
    try {
      then = value.then;
    } catch (e) {
      return reject(e);
    }
    
    // 如果 then 是函数,则当作 thenable 处理
    if (typeof then === "function") {
      let called = false;
      try {
        then.call(
          value,
          y => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject); // 递归调用resolvePromise处理返回值
          },
          r => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (e) {
        if (!called) reject(e);
      }
      return; // 进入 thenable 解析
    }
  }
  
  // 4. 普通值直接resolve
  resolve(value);
}

实现要点:

  • 自引用检测:防止 Promise 解析自身导致的无限循环
  • Promise 实例处理:通过 then 方法获取其最终状态
  • Thenable 对象处理:符合 Promise/A+ 规范的 thenable 解析
  • 防重复调用:使用 called 标志防止 resolve/reject 被多次调用
  • 递归解析:确保嵌套的 Promise 或 thenable 都能被正确解析

3. MyPromise 核心实现

3.1 构造函数

javascript
class MyPromise {
  constructor(executor) {
    // 设置默认状态 pending
    this.status = PROMISE_STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    const resolve = value => {
      // 状态只能改变一次
      if (this.status === PROMISE_STATUS_PENDING) {
        // 使用 resolvePromise 处理复杂的解析逻辑
        resolvePromise(
          this,
          value,
          resolvedValue => {
            this.status = PROMISE_STATUS_FULFILLED;
            this.value = resolvedValue;
            queueMicrotask(() => {
              //将成功的回调和失败的回调放到数组中,统一对应调用
              this.onFulfilledFns.forEach(fn => {
                fn(this.value);
              });
            });
          },
          reject
        );
      }
    };
    
    const reject = reason => {
      // 状态只能改变一次
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_REJECTED;
        this.reason = reason;
        queueMicrotask(() => {
          //将成功的回调和失败的回调放到数组中,统一对应调用
          this.onRejectedFns.forEach(fn => {
            fn(this.reason);
          });
        });
      }
    };

    try {
      executor(resolve, reject); // 进来立即执行 executor
    } catch (err) {
      reject(err);
    }
  }
}

实现要点:

  • 状态管理:使用常量定义三种状态,确保状态只能改变一次
  • 回调队列:使用数组存储待执行的回调函数
  • 微任务调度:使用 queueMicrotask 确保异步执行,符合规范
  • 异常处理:捕获 executor 执行时的同步异常
  • 复杂值解析:委托给 resolvePromise 函数处理

3.2 then 方法实现

javascript
// 实例方法
then(onFulfilled, onRejected) {
  return new MyPromise((resolve, reject) => {
    // 值穿透:决定"下一个" Promise 拿到 val 以后,要不要原样再往下传。 如果没传回调,就透传 value / reason
    if (typeof onFulfilled !== "function") {
      onFulfilled = val => val;
    }
    if (typeof onRejected !== "function") {
      // 直接抛出错误,实现错误穿透
      onRejected = err => {
        throw err;
      };
    }

    if (this.status === PROMISE_STATUS_FULFILLED) {
      queueMicrotask(() => tryCatchFn(onFulfilled, this.value, resolve, reject));
    } else if (this.status === PROMISE_STATUS_REJECTED) {
      queueMicrotask(() => tryCatchFn(onRejected, this.reason, resolve, reject));
    } else {
      this.onFulfilledFns.push(() => queueMicrotask(() => tryCatchFn(onFulfilled, this.value, resolve, reject)));
      this.onRejectedFns.push(() => queueMicrotask(() => tryCatchFn(onRejected, this.reason, resolve, reject)));
    }
  });
}

实现要点:

  • 链式调用:返回新的 Promise 实例
  • 值穿透:处理未传入回调函数的情况
  • 异步执行:使用 queueMicrotask 确保回调异步执行
  • 状态处理:根据当前状态决定立即执行还是加入队列
  • 异常处理:使用 tryCatchFn 统一处理回调异常

3.3 catch 和 finally 方法

javascript
// catch方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
catch(onRejected) {
  return this.then(undefined, onRejected);
}

finally(onFinally) {
  return this.then(
    val => MyPromise.resolve(onFinally()).then(() => val),
    err =>
      MyPromise.resolve(onFinally()).then(() => {
        throw err;
      })
  );
}

实现要点:

  • catch 方法:简单的 then 方法封装
  • finally 方法:无论成功失败都执行,且不改变原始值/原因
  • 异步 finally:支持 onFinally 返回 Promise 的情况

4. 静态方法实现

4.1 resolve 和 reject

javascript
// 静态方法 只需要将包裹后的值返回出去,作为后续链式调用依靠
static resolve(value) {
  return new MyPromise(resolve => resolve(value));
}

static reject(reason) {
  return new MyPromise((resolve, reject) => reject(reason));
}

实现要点:

  • 简洁实现:直接创建已解决/拒绝的 Promise
  • 值处理resolve 会通过 resolvePromise 处理复杂值

4.2 Promise.all 实现

javascript
static all(promises) {
  if (!isIterable(promises)) {
    return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
  }
  const promisesArr = Array.from(promises);
  const len = promisesArr.length;
  if (!len) return MyPromise.resolve([]);
  
  return new MyPromise((resolve, reject) => {
    const values = [];
    let count = len;
    promisesArr.forEach((promise, index) => {
      promise.then(val => {
        values[index] = val;
        if (--count === 0) resolve(values);
      }, reject);
    });
  });
}

实现要点:

  • 参数验证:检查是否为可迭代对象
  • 边界处理:空数组直接返回空数组的 resolved Promise
  • 并发执行:所有 Promise 并行执行
  • 结果顺序:保持结果与输入顺序一致
  • 快速失败:任一 Promise 失败则整体失败

4.3 Promise.allSettled 实现

javascript
static allSettled(promises) {
  if (!isIterable(promises)) {
    return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
  }
  const promisesArr = Array.from(promises);
  const len = promisesArr.length;
  if (!len) return MyPromise.resolve([]);
  
  return new MyPromise((resolve, reject) => {
    const values = [];
    let count = len;
    promisesArr.forEach((promise, index) => {
      promise.then(
        val => {
          values[index] = { status: PROMISE_STATUS_FULFILLED, value: val };
          if (--count === 0) resolve(values);
        },
        err => {
          values[index] = { status: PROMISE_STATUS_REJECTED, reason: err };
          if (--count === 0) resolve(values);
        }
      );
    });
  });
}

实现要点:

  • 永不失败:无论输入 Promise 成功失败,都会 resolve
  • 结果格式:返回包含 statusvalue/reason 的对象数组
  • 完整等待:等待所有 Promise 都 settled

4.4 Promise.race 实现

javascript
static race(promises) {
  if (!isIterable(promises)) {
    return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
  }
  const promisesArr = Array.from(promises);
  const len = promisesArr.length;
  if (!len) return MyPromise.resolve([]);
  
  return new MyPromise((resolve, reject) => {
    promisesArr.forEach(promise => {
      promise.then(resolve, reject);
    });
  });
}

实现要点:

  • 竞速机制:第一个 settled 的 Promise 决定结果
  • 状态传递:直接传递第一个 settled Promise 的状态和值

4.5 Promise.any 实现

javascript
static any(promises) {
  if (!isIterable(promises)) {
    return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
  }
  const promisesArr = Array.from(promises);
  const len = promisesArr.length;
  if (!len) return MyPromise.resolve([]);
  
  return new MyPromise((resolve, reject) => {
    const reasons = [];
    let count = len;
    promisesArr.forEach((promise, index) => {
      promise.then(resolve, err => {
        reasons[index] = err;
        if (--count === 0) {
          const AggregateError =
            globalThis.AggregateError ||
            function (errs) {
              const e = new Error("All promises rejected");
              e.errors = errs;
              return e;
            };
          reject(new AggregateError(reasons));
        }
      });
    });
  });
}

实现要点:

  • 首个成功:第一个 fulfilled 的 Promise 决定结果
  • 全部失败处理:只有所有 Promise 都失败才 reject
  • AggregateError:使用 AggregateError 收集所有错误,提供 polyfill

5. 完整代码

js
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";
// 工具函数 减少try catch 使用
function tryCatchFn(execFn, value, resolve, reject) {
  try {
    const result = execFn(value);
    resolve(result);
  } catch (err) {
    reject(err);
  }
}
// 判断对象是否可迭代
function isIterable(obj) {
  return obj !== null && obj !== undefined && typeof obj[Symbol.iterator] === "function";
}

// Promise解析程序
function resolvePromise(promise, value, resolve, reject) {
  // 1. x 与 MyPromise 相等 防止传入自身的时候,无限循环 自引用检测
  if (value === promise) {
    return reject(new TypeError("循环引用"));
  }
  // 2. 如果 value 是 MyPromise 实例
  if (value instanceof MyPromise) {
    // 把当前实例的状态挂靠到 value 上
    value.then(resolve, reject);
    return;
  }
  // 3. 如果 value 是对象或者函数
  if (value !== null && (typeof value === "object" || typeof value === "function")) {
    let then;
    try {
      then = value.then;
    } catch (e) {
      return reject(e);
    }
    // 如果 then 是函数,则当作 thenable 处理
    if (typeof then === "function") {
      let called = false;
      try {
        then.call(
          value,
          y => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject); // 递归调用resolvePromise处理返回值
          },
          r => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } catch (e) {
        if (!called) reject(e);
      }
      return; // 进入 thenable 解析
    }
  }
  // 4. 普通值直接resolve
  resolve(value);
}

class MyPromise {
  constructor(executor) {
    // 设置默认状态 pending
    this.status = PROMISE_STATUS_PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    const resolve = value => {
      // 状态只能改变一次
      if (this.status === PROMISE_STATUS_PENDING) {
        // 使用 resolvePromise 处理复杂的解析逻辑
        resolvePromise(
          this,
          value,
          resolvedValue => {
            this.status = PROMISE_STATUS_FULFILLED;
            this.value = resolvedValue;
            // console.log("resolve被调用");
            queueMicrotask(() => {
              // console.log("queueMicrotask-resolve-then");
              //将成功的回调和失败的回调放到数组中,统一对应调用
              this.onFulfilledFns.forEach(fn => {
                fn(this.value);
              });
            });
          },
          reject
        );
      }
    };
    const reject = reason => {
      // console.log("reject被调用");
      // 状态只能改变一次
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_REJECTED;
        this.reason = reason;
        // console.log("reject被调用");
        queueMicrotask(() => {
          // console.log("queueMicrotask-reject-then");
          //将成功的回调和失败的回调放到数组中,统一对应调用
          this.onRejectedFns.forEach(fn => {
            fn(this.reason);
          });
        });
      }
    };

    try {
      executor(resolve, reject); // 进来立即执行 executor
    } catch (err) {
      reject(err);
    }
  }

  // 实例方法
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      // 值穿透:决定“下一个” Promise 拿到 val 以后,要不要原样再往下传。 如果没传回调,就透传 value / reason
      if (typeof onFulfilled !== "function") {
        onFulfilled = val => val;
      }
      if (typeof onRejected !== "function") {
        // 直接抛出错误,实现错误穿透
        onRejected = err => {
          throw err;
        };
      }

      if (this.status === PROMISE_STATUS_FULFILLED) {
        queueMicrotask(() => tryCatchFn(onFulfilled, this.value, resolve, reject));
      } else if (this.status === PROMISE_STATUS_REJECTED) {
        queueMicrotask(() => tryCatchFn(onRejected, this.reason, resolve, reject));
      } else {
        this.onFulfilledFns.push(() => queueMicrotask(() => tryCatchFn(onFulfilled, this.value, resolve, reject)));
        this.onRejectedFns.push(() => queueMicrotask(() => tryCatchFn(onRejected, this.reason, resolve, reject)));
      }
    });
  }

  // catch方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  finally(onFinally) {
    return this.then(
      val => MyPromise.resolve(onFinally()).then(() => val),
      err =>
        MyPromise.resolve(onFinally()).then(() => {
          throw err;
        })
    );
    // return this.then(onFinally, onFinally); // 错误的
  }

  // 静态方法 只需要将包裹后的值返回出去,作为后续链式调用依靠
  static resolve(value) {
    return new MyPromise(resolve => resolve(value));
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason));
  }

  static all(promises) {
    if (!isIterable(promises)) {
      return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
    }
    const promisesArr = Array.from(promises);
    const len = promisesArr.length;
    if (!len) return MyPromise.resolve([]);
    return new MyPromise((resolve, reject) => {
      const values = [];
      let count = len;
      promisesArr.forEach((promise, index) => {
        promise.then(val => {
          values[index] = val;
          if (--count === 0) resolve(values);
        }, reject);
      });
    });
  }

  static allSettled(promises) {
    if (!isIterable(promises)) {
      return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
    }
    // 问题关键: 什么时候要执行resolve, 什么时候要执行reject
    const promisesArr = Array.from(promises);
    const len = promisesArr.length;
    if (!len) return MyPromise.resolve([]);
    return new MyPromise((resolve, reject) => {
      const values = [];
      let count = len;
      promisesArr.forEach((promise, index) => {
        promise.then(
          val => {
            values[index] = { status: PROMISE_STATUS_FULFILLED, value: val };
            if (--count === 0) resolve(values);
          },
          err => {
            values[index] = { status: PROMISE_STATUS_REJECTED, reason: err };
            if (--count === 0) resolve(values);
          }
        );
      });
    });
  }

  static race(promises) {
    if (!isIterable(promises)) {
      return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
    }
    const promisesArr = Array.from(promises);
    const len = promisesArr.length;
    if (!len) return MyPromise.resolve([]);
    return new MyPromise((resolve, reject) => {
      promisesArr.forEach(promise => {
        promise.then(resolve, reject);
      });
    });
  }

  static any(promises) {
    if (!isIterable(promises)) {
      return MyPromise.reject(new TypeError("参数必须是一个可迭代对象"));
    }
    // resolve必须等到有一个成功的结果
    // reject所有的都失败才执行reject
    const promisesArr = Array.from(promises);
    const len = promisesArr.length;
    if (!len) return MyPromise.resolve([]);
    return new MyPromise((resolve, reject) => {
      const reasons = [];
      let count = len;
      promisesArr.forEach((promise, index) => {
        promise.then(resolve, err => {
          reasons[index] = err;
          if (--count === 0) {
            const AggregateError =
              globalThis.AggregateError ||
              function (errs) {
                const e = new Error("All promises rejected");
                e.errors = errs;
                return e;
              };
            reject(new AggregateError(reasons));
          }
        });
      });
    });
  }
}

module.exports = MyPromise;

6. 设计亮点

6.1 规范遵循

  • 严格遵循 Promise/A+ 规范
  • 正确的异步执行时机(微任务)
  • 完整的 thenable 处理逻辑

6.2 代码质量

  • 模块化设计,职责分离
  • 统一的错误处理机制
  • 完善的边界情况处理

6.3 性能优化

  • 使用 queueMicrotask 而非 setTimeout
  • 避免不必要的 Promise 包装
  • 高效的回调队列管理

7. 总结

该 MyPromise 实现了 Promise/A+ 规范,并通过了全部872个测试用例,具有以下特点:

  1. 完整性:实现了所有核心功能和主要静态方法
  2. 规范性:严格遵循 Promise/A+ 规范
  3. 健壮性:完善的错误处理和边界情况处理
  4. 可维护性:清晰的代码结构和充分的注释
  5. 性能:使用了正确的异步调度机制

这个实现可以作为学习 Promise 内部机制的优秀范例,也可以在实际项目中作为 Promise 的 polyfill 使用。

Released under the MIT License.