实现一个Promise

60 阅读4分钟

让我们一起构建一个简化的 Promise 版本,并讲解其核心原理。

Promise 的核心思想

Promise 代表一个异步操作的最终结果。它可以处于三种状态:

  • Pending(进行中): 初始状态,表示异步操作尚未完成。
  • Fulfilled(已成功): 表示异步操作成功完成,并带有一个结果值。
  • Rejected(已失败): 表示异步操作失败,并带有一个错误原因。

Promise 允许你使用 then() 方法来注册回调函数,这些回调函数会在 Promise 状态改变时被调用。

简化的 Promise 实现

function MyPromise(executor) {
  this.status = 'pending';  // 初始状态为 pending
  this.value = undefined;   // 存储成功的结果
  this.reason = undefined;  // 存储失败的原因
  this.onFulfilledCallbacks = []; // 存储成功的回调函数
  this.onRejectedCallbacks = [];  // 存储失败的回调函数

  const resolve = (value) => {
    if (this.status === 'pending') {
      this.status = 'fulfilled';
      this.value = value;
      this.onFulfilledCallbacks.forEach(callback => callback(this.value));
    }
  };

  const reject = (reason) => {
    if (this.status === 'pending') {
      this.status = 'rejected';
      this.reason = reason;
      this.onRejectedCallbacks.forEach(callback => callback(this.reason));
    }
  };

  try {
    executor(resolve, reject); // 执行传入的 executor 函数
  } catch (error) {
    reject(error);           // 捕获 executor 执行过程中的错误
  }
}

MyPromise.prototype.then = function(onFulfilled, onRejected) {
  // 如果 onFulfilled 不是函数,提供一个默认函数
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
  // 如果 onRejected 不是函数,提供一个抛出错误的默认函数
  onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

  let promise2; // 用于返回新的 Promise 

  if (this.status === 'fulfilled') {
    promise2 = new MyPromise((resolve, reject) => {
      setTimeout(() => { // 使用 setTimeout 确保异步执行
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject); // 处理 then 返回值
        } catch (error) {
          reject(error);
        }
      });
    });
  }

  if (this.status === 'rejected') {
    promise2 = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      });
    });
  }

  if (this.status === 'pending') {
    promise2 = new MyPromise((resolve, reject) => {
      this.onFulfilledCallbacks.push((value) => {
        setTimeout(() => {
          try {
            const x = onFulfilled(value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      });
      this.onRejectedCallbacks.push((reason) => {
        setTimeout(() => {
          try {
            const x = onRejected(reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      });
    });
  }

  return promise2;
};

function resolvePromise(promise2, x, resolve, reject) {
  // 防止循环引用
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 如果 x 是一个 Promise,根据它的状态决定 promise2 的状态
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) { 
    // 如果 x 是对象或函数,尝试提取 then 方法
    let called = false;
    try {
      const then = x.then;
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, r => {
          if (called) return;
          called = true;
          reject(r);
        });
      } else {
        resolve(x); // 如果 x 不是 thenable,直接 resolve
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    resolve(x); // x 不是对象或函数,直接 resolve
  }
}

// 测试
const promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success!');
  }, 1000);
});

promise.then(value => {
  console.log(value); // 输出: Success!
  return 'Another value';
}).then(value => {
  console.log(value); // 输出: Another value
});

原理讲解

  1. 构造函数 MyPromise:

    • 初始化 Promise 的状态为 pending,结果 value 和原因 reasonundefined
    • 创建两个数组 onFulfilledCallbacksonRejectedCallbacks 用于存储成功和失败的回调函数。
    • 接收一个 executor 函数作为参数,该函数会在创建 Promise 时立即执行。
    • executor 函数接收两个参数 resolvereject,用于改变 Promise 的状态。
    • resolve 函数将状态改为 fulfilled,设置结果 value,并执行所有 onFulfilledCallbacks 中的回调函数。
    • reject 函数将状态改为 rejected,设置原因 reason,并执行所有 onRejectedCallbacks 中的回调函数。
    • 使用 try...catch 捕获 executor 执行过程中可能出现的错误,并调用 reject 抛出错误。
  2. then 方法:

    • 接收两个可选参数 onFulfilledonRejected,分别表示成功和失败的回调函数。
    • 如果 onFulfilledonRejected 不是函数,则提供默认函数。
    • 创建一个新的 Promise promise2 作为 then 方法的返回值,用于实现链式调用。
    • 根据当前 Promise 的状态:
      • 如果是 fulfilled,则异步执行 onFulfilled 回调函数,并将返回值传递给 resolvePromise 处理。
      • 如果是 rejected,则异步执行 onRejected 回调函数,并将返回值传递给 resolvePromise 处理。
      • 如果是 pending,则将 onFulfilledonRejected 回调函数分别添加到 onFulfilledCallbacksonRejectedCallbacks 数组中,等待 Promise 状态改变时执行。
    • 返回 promise2
  3. resolvePromise 函数:

    • 处理 then 方法中回调函数的返回值,决定 promise2 的最终状态和结果。
    • 防止循环引用:如果 promise2x 是同一个对象,则抛出类型错误。
    • 如果 x 是一个 Promise,则根据 x 的状态来改变 promise2 的状态。
    • 如果 x 是一个 thenable 对象(具有 then 方法的对象或函数),则尝试调用 then 方法,根据 then 的回调结果来改变 promise2 的状态。
    • 如果 x 不是 thenable,则直接将 promise2 resolve 为 x 的值。

重要概念

  • 异步执行: 使用 setTimeout 确保回调函数在事件循环的下一个循环中执行,从而实现异步。
  • 链式调用: then 方法返回一个新的 Promise,使得可以链式调用多个 then 方法。
  • 错误处理: 使用 try...catch 捕获错误,并在 reject 中传递错误原因。

这个简化的 Promise 实现涵盖了 Promise 的核心原理,但它并不包含所有细节。例如,实际的 Promise 实现还需要处理更复杂的错误情况和微任务队列。 然而,通过理解这个简单的实现,你可以更好地理解 Promise 的工作方式。