手写 Promise 实现

1. 核心状态维护

Promise 有三种状态:PENDINGFULFILLEDREJECTED。 必须确保状态转换不可逆。

2. 完整实现 (面试版)

这是一个符合 Promise/A+ 规范的迷你版本,足以应对大部分手写题。

/**
 * 实现一个Promise
 * Promise 有三种状态:pending、fulfilled、rejected
 * 1. 状态只能从 pending 变为 fulfilled 或者 rejected,一经改变,状态就不能再变化
 * 2. 执行构造函数时,executor 立即执行
 * 3. 执行 executor 出错,立马将状态改为 rejected
 */
class MyPromise {
  // 创建一个构造函数,构造函数执行的时候 executor 立即执行
  constructor(executor) {
    this._status = MyPromise.PENDING;
    this._value = undefined;
    // 保存函数回调的队列
    this._handlers = [];

    try {
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      // 任务出错了,直接将状态改为 rejected
      this._reject(error);
    }
  }

  /**
   * 1. 表示当前任务完成
   * 2. 改变 promise 状态为 fulfilled
   * 3. 记录成功的数据
   * @param {*} data
   */
  _resolve(data) {
    if (this._status !== MyPromise.PENDING) return;
    this._status = MyPromise.FULFILLED;
    this._value = data;

    // 状态变化时,执行处理队列中的函数
    this._runHandlers();
  }

  /**
   * 1. 表示当前任务失败
   * 2. 改变 promise 状态为 rejected
   * 3. 记录失败的原因
   * @param {*} reason
   * @returns
   */
  _reject(reason) {
    if (this._status !== MyPromise.PENDING) return;
    this._status = MyPromise.REJECTED;
    this._value = reason;

    // 状态变化时,执行处理队列中的函数
    this._runHandlers();
  }

  /**
   * 返回一个 Promise
   * @param {*} onFulfilled
   * @param {*} onRejected
   * @returns
   */
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._handlers.push({ executor: onFulfilled, state: MyPromise.FULFILLED, resolve, reject });
      this._handlers.push({ executor: onRejected, state: MyPromise.REJECTED, resolve, reject });

      // 如果状态已决,则直接执行
      this._runHandlers();
    });
  }

  /**
   * 执行处理队列中的函数
   */
  _runHandlers() {
    // 如果状态还是 pending 挂起状态,不执行
    if (this._status === MyPromise.PENDING) return;

    // 每处理一个函数,都需要将其从队列中移除
    while (this._handlers[0]) {
      this._runHandler(this._handlers[0]);
      this._handlers.shift();
    }
  }

  _runHandler({ executor, state, resolve, reject }) {
    runMicroTask(() => {
      // 如果状态不一致,则不处理;
      // onFulfilled 处理状态为 fulfilled 的函数
      // onRejected 处理状态为 rejected 的函数
      if (state !== this._status) return;

      // 如果 onFulfilled 和 onRejected 不是函数,则需要将状态透传
      if (typeof executor !== "function") {
        this._status === MyPromise.FULFILLED ? resolve(this._value) : reject(this._value);
        return;
      }

      try {
        const result = executor(this._value);
        // 如果返回值是 Promise,则等待其状态变化
        if (isPromise(result)) {
          result.then(resolve, reject);
        } else {
          // 如果返回值不是 Promise,则直接返回
          resolve(result);
        }
      } catch (error) {
        reject(error);
      }
    });
  }

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

  finally(onFinally) {
    return this.then(
      (data) => {
        onFinally();
        return data;
      },
      (reason) => {
        onFinally();
        throw reason;
      },
    );
  }
}

MyPromise.PENDING = "pending";
MyPromise.FULFILLED = "fulfilled";
MyPromise.REJECTED = "rejected";

function runMicroTask(fn) {
  process.nextTick(fn);
}

function isPromise(obj) {
  return !!(obj && typeof obj === "object" && typeof obj.then === "function");
}

3. 静态方法实现

Promise.resolve

static resolve(data) {
    if (data instanceof MyPromise) return data;

    return new MyPromise((resolve, reject) => {
      if (isPromise(data)) {
        data.then(resolve, reject);
      } else {
        resolve(data);
      }
    });
  }

Promise.reject

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

Promise.all

/**
   * 返回一个新的 Promise
   * 所有 promise 成功后才返回成功,只要有一个失败就返回失败
   * 返回结果的顺序需要保持一致
   * @param {*} promises
   */
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      const result = [];
      let count = 0;
      let fulfilledCount = 0;

      for (let pro of promises) {
        // 记录当前的索引, 用于保证结果的顺序
        let i = count;
        count++;
        // 用 resolve 包装一下,保证每个 promise 都是 promise 实例
        MyPromise.resolve(pro).then((data) => {
          result[i] = data;
          fulfilledCount++;
          if (fulfilledCount === count) {
            resolve(result);
          }
        }, reject);
      }

      if (count === 0) {
        resolve(result);
      }
    });
  }

Promise.allSettled

/**
   * 返回一个新的 Promise
   * 等待所有的 promise 都 settled 后返回结果
   * @param {*} promises
   */
  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      const result = [];
      let count = 0;
      let fulfilledCount = 0;

      for (let pro of promises) {
        let i = count;
        count++;
        MyPromise.resolve(pro).then(
          (data) => {
            result[i] = { status: MyPromise.FULFILLED, value: data };
            fulfilledCount++;
            if (fulfilledCount === count) {
              resolve(result);
            }
          },
          (reason) => {
            result[i] = { status: MyPromise.REJECTED, reason };
            fulfilledCount++;
            if (fulfilledCount === count) {
              resolve(result);
            }
          },
        );
      }

      if (count === 0) {
        resolve(result);
      }
    });
  }

Promise.race

static race(promises) {
    return new MyPromise((resolve, reject) => {
      for (let pro of promises) {
        MyPromise.resolve(pro).then(resolve, reject);
      }
    });
}

4. 关键考点

  1. Pending 状态不可逆
  2. 异步执行:必须使用 queueMicrotasksetTimeout 来保证 then 回调在当前宏任务之后执行。
  3. 链式调用:返回一个新的 Promise。
  4. 值穿透:如果没有传 onFulfilled,需要透传值 (x => x)。