Promise 实现-详细

从零开始手写一个符合 Promise/A+ 规范的 Promise 实现,理解 Promise 的原理和异步处理机制

问题

Promise 是 JavaScript 中处理异步操作的重要机制。本题要求手写一个完整的 Promise 实现,需要满足以下要求:

  1. 实现 Promise 的三种状态:pending、fulfilled、rejected
  2. 实现 then 方法,支持链式调用
  3. 实现 catch、finally 方法
  4. 实现静态方法:resolve、reject、all、race、allSettled、any
  5. 符合 Promise/A+ 规范

解答

// Promise 的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    this.state = PENDING; // 初始状态
    this.value = undefined; // 成功的值
    this.reason = undefined; // 失败的原因
    this.onFulfilledCallbacks = []; // 成功回调队列
    this.onRejectedCallbacks = []; // 失败回调队列

    // 成功时调用
    const resolve = (value) => {
      // 只有 pending 状态才能转换
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;
        // 执行所有成功回调
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    // 失败时调用
    const reject = (reason) => {
      // 只有 pending 状态才能转换
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        // 执行所有失败回调
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

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

  // then 方法
  then(onFulfilled, onRejected) {
    // 参数可选,提供默认值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    // 返回新的 Promise 实现链式调用
    const promise2 = new MyPromise((resolve, reject) => {
      if (this.state === FULFILLED) {
        // 使用 setTimeout 模拟微任务
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      if (this.state === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            this.resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      if (this.state === PENDING) {
        // 异步情况,将回调存入队列
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }

  // 处理 then 返回值
  resolvePromise(promise2, x, resolve, reject) {
    // 不能返回自身,避免循环引用
    if (promise2 === x) {
      return reject(new TypeError('Chaining cycle detected'));
    }

    // 如果 x 是 Promise 实例
    if (x instanceof MyPromise) {
      x.then(resolve, reject);
    } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      // 如果 x 是对象或函数
      let called = false; // 防止多次调用
      try {
        const then = x.then;
        if (typeof then === 'function') {
          // x 是 thenable 对象
          then.call(
            x,
            y => {
              if (called) return;
              called = true;
              this.resolvePromise(promise2, y, resolve, reject);
            },
            r => {
              if (called) return;
              called = true;
              reject(r);
            }
          );
        } else {
          resolve(x);
        }
      } catch (error) {
        if (called) return;
        called = true;
        reject(error);
      }
    } else {
      // 普通值直接 resolve
      resolve(x);
    }
  }

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

  // finally 方法
  finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),
      reason => MyPromise.resolve(callback()).then(() => { throw reason })
    );
  }

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

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

  // 静态 all 方法
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }

      const results = [];
      let count = 0;
      const len = promises.length;

      if (len === 0) {
        return resolve(results);
      }

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = value;
            count++;
            if (count === len) {
              resolve(results);
            }
          },
          reason => {
            reject(reason);
          }
        );
      });
    });
  }

  // 静态 race 方法
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }

      promises.forEach(promise => {
        MyPromise.resolve(promise).then(resolve, reject);
      });
    });
  }

  // 静态 allSettled 方法
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }

      const results = [];
      let count = 0;
      const len = promises.length;

      if (len === 0) {
        return resolve(results);
      }

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            results[index] = { status: 'fulfilled', value };
            count++;
            if (count === len) {
              resolve(results);
            }
          },
          reason => {
            results[index] = { status: 'rejected', reason };
            count++;
            if (count === len) {
              resolve(results);
            }
          }
        );
      });
    });
  }

  // 静态 any 方法
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Argument must be an array'));
      }

      const errors = [];
      let count = 0;
      const len = promises.length;

      if (len === 0) {
        return reject(new AggregateError([], 'All promises were rejected'));
      }

      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(
          value => {
            resolve(value);
          },
          reason => {
            errors[index] = reason;
            count++;
            if (count === len) {
              reject(new AggregateError(errors, 'All promises were rejected'));
            }
          }
        );
      });
    });
  }
}

使用示例

// 基本使用
const promise1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功');
  }, 1000);
});

promise1.then(value => {
  console.log(value); // 输出: 成功
  return '链式调用';
}).then(value => {
  console.log(value); // 输出: 链式调用
});

// catch 使用
const promise2 = new MyPromise((resolve, reject) => {
  reject('失败');
});

promise2.catch(error => {
  console.log(error); // 输出: 失败
});

// finally 使用
const promise3 = new MyPromise((resolve, reject) => {
  resolve('完成');
});

promise3
  .then(value => console.log(value))
  .finally(() => console.log('清理工作'));

// Promise.all 使用
MyPromise.all([
  MyPromise.resolve(1),
  MyPromise.resolve(2),
  MyPromise.resolve(3)
]).then(results => {
  console.log(results); // 输出: [1, 2, 3]
});

// Promise.race 使用
MyPromise.race([
  new MyPromise(resolve => setTimeout(() => resolve(1), 1000)),
  new MyPromise(resolve => setTimeout(() => resolve(2), 500))
]).then(result => {
  console.log(result); // 输出: 2
});

// Promise.allSettled 使用
MyPromise.allSettled([
  MyPromise.resolve(1),
  MyPromise.reject('error'),
  MyPromise.resolve(3)
]).then(results => {
  console.log(results);
  // 输出: [
  //   { status: 'fulfilled', value: 1 },
  //   { status: 'rejected', reason: 'error' },
  //   { status: 'fulfilled', value: 3 }
  // ]
});

// Promise.any 使用
MyPromise.any([
  MyPromise.reject('error1'),
  MyPromise.resolve(2),
  MyPromise.reject('error2')
]).then(result => {
  console.log(result); // 输出: 2
});

关键点

  • 状态管理:Promise 有三种状态(pending、fulfilled、rejected),状态一旦改变就不可逆
  • 回调队列:使用数组存储异步情况下的回调函数,状态改变时统一执行
  • then 方法链式调用:每次调用 then 都返回一个新的 Promise 实例
  • resolvePromise 方法:处理 then 回调的返回值,支持返回普通值、Promise 实例或 thenable 对象
  • 循环引用检测:防止 then 返回自身导致的死循环
  • 异步执行:使用 setTimeout 模拟微任务,确保回调异步执行
  • 错误处理:executor 执行错误、回调执行错误都需要捕获并 reject
  • 静态方法实现:all、race、allSettled、any 等方法需要正确处理多个 Promise 的并发场景
  • 参数校验:对传入的参数进行类型检查,提供默认值或抛出错误
  • thenable 对象处理:支持具有 then 方法的对象,实现 Promise 互操作性