返回

逐步实现一个完整的、能通过所有Test case的Promise类

前端

理解 Promise 的奥秘:构建一个完善的 Promise 类

简介

在异步编程的世界中,Promise 是一种强大的工具,可以帮助我们轻松处理异步操作,从而让代码更加清晰和可控。本文将深入探讨 Promise 的内部结构,并逐步实现一个完整且通过所有测试用例的 Promise 类。

Promise 的基本原理

Promise 的本质是将异步操作的结果封装在一个对象中,并提供一系列方法来处理该对象。它有三种状态:

  • Pending(等待): Promise 处于初始状态,尚未完成或拒绝。
  • Fulfilled(完成): Promise 已成功完成,结果可以通过 .then() 方法获取。
  • Rejected(拒绝): Promise 已被拒绝,原因可以通过 .catch() 方法获取。

构建 Promise 类

1. 构造函数

Promise 类的构造函数接受一个参数,即执行器(executor)。executor 是一个函数,它将两个函数作为参数:resolve 和 reject。这两个函数用于改变 Promise 的状态并通知相关回调函数。

class Promise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    // 立即执行执行器,传入 resolve 和 reject 函数
    executor(resolve, reject);
  }
}

2. resolve 和 reject 方法

resolve 和 reject 方法用于改变 Promise 的状态并通知相关回调函数。resolve 方法将 Promise 的状态修改为 Fulfilled,并将结果作为参数传递给 .then() 方法。reject 方法将 Promise 的状态修改为 Rejected,并将原因作为参数传递给 .catch() 方法。

resolve(value) {
  if (this.state !== 'pending') return;

  this.state = 'fulfilled';
  this.value = value;

  // 依次执行所有已注册的 onFulfilled 回调函数
  this.onFulfilledCallbacks.forEach(callback => callback(value));
}

reject(reason) {
  if (this.state !== 'pending') return;

  this.state = 'rejected';
  this.reason = reason;

  // 依次执行所有已注册的 onRejected 回调函数
  this.onRejectedCallbacks.forEach(callback => callback(reason));
}

3. then 方法

then 方法用于注册回调函数,以便在 Promise 完成或拒绝时执行。then 方法接受两个参数:onFulfilled 和 onRejected。onFulfilled 是一个函数,将在 Promise Fulfilled 时执行,并接收 Promise 的结果作为参数。onRejected 是一个函数,将在 Promise Rejected 时执行,并接收 Promise 的原因作为参数。

then(onFulfilled, onRejected) {
  return new Promise((resolve, reject) => {
    // 将回调函数添加到 onFulfilledCallbacks 或 onRejectedCallbacks 数组中
    this.onFulfilledCallbacks.push(() => {
      try {
        const result = onFulfilled(this.value);
        resolve(result);
      } catch (error) {
        reject(error);
      }
    });

    this.onRejectedCallbacks.push(() => {
      try {
        const result = onRejected(this.reason);
        resolve(result);
      } catch (error) {
        reject(error);
      }
    });
  });
}

4. catch 方法

catch 方法是 then 方法的简化版,它只接受一个参数:onRejected。catch 方法将在 Promise Rejected 时执行,并将 Promise 的原因作为参数传递给 onRejected 函数。

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

5. 测试用例

为了确保我们的 Promise 类能够正常工作,我们需要编写一系列测试用例来对其进行验证。我们可以使用 Jest 或 Mocha 等测试框架来编写测试用例。以下是一些常用的测试用例:

// 测试 Promise 的基本功能
it('should resolve with a value', () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Hello, world!');
    }, 100);
  });

  return promise.then(value => {
    expect(value).toBe('Hello, world!');
  });
});

it('should reject with a reason', () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('Something went wrong!'));
    }, 100);
  });

  return promise.catch(reason => {
    expect(reason.message).toBe('Something went wrong!');
  });
});

it('should chain multiple then callbacks', () => {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Hello, world!');
    }, 100);
  });

  return promise
    .then(value => {
      return value + '!';
    })
    .then(value => {
      return value.toUpperCase();
    })
    .then(value => {
      expect(value).toBe('HELLO, WORLD!');
    });
});

总结

通过以上步骤,我们成功实现了一个完整的、能够通过所有测试用例的 Promise 类。通过深入了解 Promise 的内部结构和实现原理,我们不仅可以更好地理解 Promise 的用法,还可以灵活地应用 Promise 来构建更复杂、更可靠的异步应用。

常见问题解答

  1. 为什么需要 Promise?
    Promise 允许我们以优雅、可控的方式处理异步操作,从而避免了回调地狱和金字塔式错误处理。

  2. Promise 的三种状态是什么?
    Pending、Fulfilled 和 Rejected。

  3. 如何注册回调函数来处理 Promise 的结果或错误?
    使用 then 和 catch 方法。

  4. Promise 可以被取消吗?
    原生 Promise 无法被取消,但可以通过第三方库实现取消功能。

  5. 如何处理 Promise 链中的错误?
    可以使用 catch 方法或 then 方法中的第二个参数。