返回

用原生 JavaScript 构建 MyPromise,深入理解 Promise②

前端

了解 Promise 的工作原理对于任何认真的 JavaScript 开发人员来说都至关重要。掌握 Promise 的奥秘可以显着提高你的异步编程技能,并让你的代码更加优雅和可维护。

揭开 Promise 的面纱

在深入了解 MyPromise 之前,让我们回顾一下 Promise 的基本概念。Promise 是一个对象,表示一个异步操作的最终结果。它可以处于三种状态之一:

  • Pending: 初始状态,表示操作尚未完成。
  • Fulfilled: 操作成功完成,结果可以通过 .then() 方法获取。
  • Rejected: 操作失败,拒绝的原因可以通过 .catch() 方法获取。

Promise 的强大之处在于它允许我们处理异步操作而不阻塞主线程。我们可以链式调用 .then().catch() 方法来处理 Promise 的结果,并执行后续操作。

构建 MyPromise

现在,我们准备动手构建我们的 MyPromise。首先,我们定义一个 executor 函数,它接受 resolve 和 reject 两个回调函数作为参数:

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

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
}

executor 函数在创建 MyPromise 实例时立即执行。它可以将 Promise 标记为 fulfilled 或 rejected 状态,并提供相应的值或拒绝原因。

接下来,我们定义 resolve 和 reject 方法:

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

  this.state = "fulfilled";
  this.value = value;
  this.onFulfilledCallbacks.forEach(callback => callback(this.value));
}

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

  this.state = "rejected";
  this.reason = reason;
  this.onRejectedCallbacks.forEach(callback => callback(this.reason));
}

resolve 方法将 Promise 标记为 fulfilled 状态并提供一个值,而 reject 方法将 Promise 标记为 rejected 状态并提供一个拒绝原因。

处理 Promise 的结果

我们还需要定义 .then().catch() 方法来处理 Promise 的结果:

then(onFulfilled, onRejected) {
  if (typeof onFulfilled !== "function") onFulfilled = value => value;
  if (typeof onRejected !== "function") onRejected = reason => { throw reason; };

  const promise2 = new MyPromise((resolve, reject) => {
    if (this.state === "fulfilled") {
      setTimeout(() => {
        try {
          const x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else if (this.state === "rejected") {
      setTimeout(() => {
        try {
          const x = onRejected(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else {
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }
  });

  return promise2;
}

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

.then() 方法接受两个回调函数:onFulfilled 和 onRejected。如果 Promise 处于 fulfilled 状态,则调用 onFulfilled 回调函数,并将结果值传递给链式 Promise。如果 Promise 处于 rejected 状态,则调用 onRejected 回调函数,并将拒绝原因传递给链式 Promise。

符合 Promises/A+ 规范

为了确保 MyPromise 符合 Promises/A+ 规范,我们必须实现一些额外的规则,例如:

  • Promise 的状态只能从 pending 转换到 fulfilled 或 rejected。
  • Promise 必须异步地执行其 onFulfilled 和 onRejected 回调函数。
  • 如果 onFulfilled 或 onRejected 回调函数抛出错误,则链式 Promise 应拒绝该错误。
  • 如果 resolve 和 reject 多次调用,则 Promise 的状态只应在第一次调用时更改。
  • 如果 resolve 和 reject 都调用,则 Promise 的状态应为 rejected,拒绝原因应为 reject 的最后一个参数。

真实世界的示例

让我们通过一个实际示例来展示 MyPromise 的强大功能:

const myPromise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    const randomNumber = Math.random();

    if (randomNumber > 0.5) {
      resolve("成功!");
    } else {
      reject("失败!");
    }
  }, 2000);
});

myPromise
  .then(result => {
    console.log(result); // "成功!"
  })
  .catch(error => {
    console.error(error); // "失败!"
  });

在这个示例中,我们创建了一个 MyPromise,它将在 2 秒后随机解析为 "成功!" 或 "失败!"。.then().catch() 方法用于处理 Promise 的结果并分别记录成功或失败消息。

结论

通过构建自己的 MyPromise,我们深入了解了 Promise 的内部机制。我们探索了 Promise 的状态、处理结果的方法,并确保我们实现的 MyPromise 符合 Promises/A+ 规范。通过实践这些概念,你可以大大提高你的 JavaScript 技能,并编写出更优雅和可维护的代码。