实现 Promise

基础知识

Promise 是 JavaScript 中用于处理异步操作的核心解决方案。它是一个对象,代表了一个尚未完成但最终会完成(或失败)的操作。理解并手动实现一个 Promise,是深入掌握 JS 异步编程的必经之路。

  1. 三种状态 (States): 每个 Promise 实例都必须处于以下三种状态之一:

    • Pending (进行中): 初始状态,既不是成功,也不是失败。
    • Fulfilled (已成功): 意味着操作成功完成。
    • Rejected (已失败): 意味着操作失败。 一个 Promise 的状态一旦从 pending 变为 fulfilledrejected,就称之为 Settled (已敲定),其状态将永远不会再改变。
  2. 执行器函数 (Executor Function): Promise 的构造函数接收一个函数作为参数,我们称之为“执行器”。这个函数会立即执行,并接收两个由 Promise 引擎提供的函数作为参数:resolvereject

    • resolve(value): 在异步操作成功时调用,将 Promise 的状态从 pending 变为 fulfilled,并将成功的值 value 传递下去。
    • reject(reason): 在异步操作失败时调用,将 Promise 的状态从 pending 变为 rejected,并将失败的原因 reason 传递下去。
  3. .then() 方法: 这是 Promise 最核心的方法,用于注册在 Promise 状态敲定后需要执行的回调函数。.then(onFulfilled, onRejected) 接收两个可选的函数参数。.then() 方法必须返回一个新的 Promise,这是实现链式调用的关键。

核心思路

我们的目标是创建一个 MyPromise 类,模拟原生 Promise 的行为。其内部结构和逻辑可以分解为以下几个关键部分:

  1. 状态机管理: 在 MyPromise 内部,需要一个变量(如 this.state)来管理三种状态。状态的转变必须是单向的(从 pendingfulfilledrejected)。

  2. 结果存储: 需要变量来存储成功时的值(value)和失败时的原因(reason)。这个值一旦被设置,就不再改变。

  3. 回调队列: 这是实现异步的关键。因为 .then() 可以在 Promise 状态敲定之前被调用,所以我们需要两个数组(如 onFulfilledCallbacksonRejectedCallbacks)来暂存这些回调函数。当 Promise 状态最终改变时(即 resolvereject被调用),再依次执行队列中的所有回调。

  4. .then() 的实现:

    • .then() 必须返回一个新的 Promise (promise2)。
    • 当调用 .then() 时,检查当前 Promise 的状态:
      • 如果状态是 pending,则将 onFulfilledonRejected 回调存入队列。
      • 如果状态已经是 fulfilledrejected,则异步地执行相应的回调。
    • 链式调用的核心promise2 的状态由 onFulfilledonRejected 的执行结果决定。
      • 如果回调函数执行时抛出异常,promise2 必须被 reject
      • 如果回调函数返回一个值,promise2 会用这个值 fulfill
      • (高级) 如果回调函数返回的是另一个 Promise,promise2 的状态将与这个 Promise 的状态保持一致。
  5. 异步执行回调: Promise/A+ 规范要求 .then() 中的回调必须是异步执行的。我们可以使用 setTimeout(..., 0) 来模拟这个“微任务”的行为。

代码实现

下面是一个遵循核心思路的 MyPromise 实现。它包含了核心功能,但为简化理解,省略了 Promise/A+ 规范中部分复杂的解析流程。

// 定义三种状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  constructor(executor) {
    // 1. 初始化状态、值和原因
    this.state = PENDING;
    this.value = undefined;
    this.reason = undefined;

    // 2. 初始化成功和失败的回调队列
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    // 3. 定义 resolve 函数
    const resolve = (value) => {
      // 只有在 pending 状态下才能改变
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;
        // 依次执行所有成功的回调
        this.onFulfilledCallbacks.forEach((fn) => fn());
      }
    };

    // 4. 定义 reject 函数
    const reject = (reason) => {
      // 只有在 pending 状态下才能改变
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;
        // 依次执行所有失败的回调
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    // 5. 立即执行 executor,并捕获可能出现的错误
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    // 6. then 方法必须返回一个新的 Promise
    const promise2 = new MyPromise((resolve, reject) => {
      // 7. 当状态为 fulfilled 时
      if (this.state === FULFILLED) {
        // 使用 setTimeout 模拟异步
        setTimeout(() => {
          try {
            // onFulfilled 必须是函数
            if (typeof onFulfilled === 'function') {
              const x = onFulfilled(this.value);
              resolve(x); // 将 onFulfilled 的返回值作为 promise2 的成功值
            } else {
              resolve(this.value); // 如果 onFulfilled 不是函数,则直接透传值
            }
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      // 8. 当状态为 rejected 时
      if (this.state === REJECTED) {
        setTimeout(() => {
          try {
            if (typeof onRejected === 'function') {
              const x = onRejected(this.reason);
              resolve(x); // 注意:.catch 后面可以接 .then,所以这里是 resolve
            } else {
              reject(this.reason); // 如果 onRejected 不是函数,则直接透传错误
            }
          } catch (error) {
            reject(error);
          }
        }, 0);
      }

      // 9. 当状态为 pending 时,将回调函数存入队列
      if (this.state === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              if (typeof onFulfilled === 'function') {
                const x = onFulfilled(this.value);
                resolve(x);
              } else {
                resolve(this.value);
              }
            } catch (error) {
              reject(error);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              if (typeof onRejected === 'function') {
                const x = onRejected(this.reason);
                resolve(x);
              } else {
                reject(this.reason);
              }
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}

代码解析

  1. constructor:初始化 statepending,并准备好存储回调的数组。
  2. resolve 和 reject 函数:这两个函数被定义在 constructor 内部。它们是改变 Promise 状态的唯一途径。核心逻辑是:
    • 检查当前状态是否为 pending(保证状态只能改变一次)。
    • 更新状态、保存结果(valuereason)。
    • 调用 forEach 执行所有已经通过 .then() 注册的、等待中的回调函数。
  3. try...catch:执行器函数 executor 是立即同步执行的。如果它在执行过程中抛出错误,这个错误应该被捕获,并用这个错误来 reject 当前的 Promise
  4. then 返回新 Promise.then 方法的核心是返回一个全新的 promise2。这是所有链式调用的基础。
  5. then 的状态处理.then 内部通过 if 判断当前 Promisethis)的状态:
    • 已敲定(Fulfilled/Rejected):如果状态已经确定,就不能立即执行回调,而是用 setTimeout(..., 0) 将其放入下一个事件循环中执行,以保证异步性。回调的返回值或抛出的错误,将决定 promise2 的命运。
    • 进行中(Pending):如果状态还是 pending,说明 resolvereject 还没被调用。此时,我们不能执行回调,而是需要将它们包装一下(包装是为了处理返回值和错误),然后 push 到对应的回调队列中,等待 resolvereject 被调用时统一执行。
  6. 链式调用的实现:在 setTimeout 的回调中,我们执行 onFulfilled(this.value) 并将其返回值 x,通过 resolve(x) 传递给 promise2。这就实现了值的传递。如果 onFulfilled 抛出异常,catch 块会捕获它,并通过 reject(error)promise2 置为失败状态。这就是链式调用中成功和失败状态的传递机制。
Copyright © Jun 2025 all right reserved,powered by Gitbook该文件修订时间: 2025-07-03 17:35:08

results matching ""

    No results matching ""

    results matching ""

      No results matching ""