返回

从零实现一个自己的Promise

前端

了解 Promise:掌控异步编程的强大工具

简介

在现代 Web 开发中,异步编程变得越来越普遍。Promise 是一种强大的工具,可以帮助我们处理异步操作,使我们的代码更易于阅读和维护。在本文中,我们将深入了解 Promise 的工作原理,并逐步构建我们自己的 Promise 实现。

Promise 的概念

Promise 是一个表示异步操作最终结果的对象。它有三种可能的状态:

  • 待处理(Pending): 操作尚未完成。
  • 已完成(Fulfilled): 操作已成功完成,并且产生了结果。
  • 已拒绝(Rejected): 操作已失败,并且产生了错误原因。

实现自己的 Promise

我们可以通过以下步骤实现我们自己的 Promise:

1. 构造函数

Promise 的构造函数接受一个称为执行器的函数,该函数立即调用以执行异步操作。执行器函数有两个参数:

  • resolve: 如果操作成功完成,则调用此函数来将 Promise 标记为已完成状态并提供结果。
  • reject: 如果操作失败,则调用此函数来将 Promise 标记为已拒绝状态并提供错误原因。

代码示例:

function MyPromise(executor) {
  this.state = 'pending';
  this.value = null;
  this.reason = null;
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];

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

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

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

2. then 方法

then 方法允许我们为 Promise 注册回调函数,这些回调函数将在 Promise 完成时调用。该方法返回一个新的 Promise,该 Promise 将根据原始 Promise 的状态被解析或拒绝。

代码示例:

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  return new MyPromise((resolve, reject) => {
    this.onFulfilledCallbacks.push(() => {
      try {
        const value = onFulfilled(this.value);
        resolve(value);
      } catch (err) {
        reject(err);
      }
    });

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

3. catch 方法

catch 方法是 then 方法的简写形式,它仅在 Promise 被拒绝时执行回调函数。

代码示例:

MyPromise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
};

4. finally 方法

finally 方法允许我们为 Promise 注册一个回调函数,无论 Promise 是已完成还是已拒绝,该回调函数都将被调用。

代码示例:

MyPromise.prototype.finally = function (onFinally) {
  return this.then(
    (value) => {
      onFinally();
      return value;
    },
    (reason) => {
      onFinally();
      throw reason;
    }
  );
};

使用 Promise

现在我们已经实现了自己的 Promise,让我们看看如何使用它:

const myPromise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    if (Math.random() > 0.5) {
      resolve('成功!');
    } else {
      reject('失败了!');
    }
  }, 1000);
});

myPromise
  .then((result) => {
    console.log('结果:', result);
  })
  .catch((error) => {
    console.log('错误:', error);
  });

在这段代码中,我们创建了一个 Promise,它在 1 秒后以 50% 的概率随机解析或拒绝。然后,我们使用 thencatch 方法来处理 Promise 的结果。

结论

通过构建自己的 Promise 实现,我们不仅加深了对 Promise 工作原理的理解,而且还获得了一个可以在我们的项目中使用的强大工具。Promise 使我们能够以优雅且可控的方式处理异步操作,从而提高了我们的代码的可读性和可维护性。

常见问题解答

1. Promise 和回调函数有什么区别?

回调函数是在异步操作完成后立即执行的函数。Promise 是对异步操作最终结果的表示,它允许我们以链式方式处理结果。

2. 如何处理嵌套 Promise?

我们可以使用 then 方法来处理嵌套 Promise,该方法返回一个新的 Promise。嵌套 Promise 的结果将作为外部 Promise 的结果。

3. 如何处理未处理的拒绝 Promise?

未处理的拒绝 Promise 会导致控制台错误。我们可以使用 Promise.unhandledRejection 事件来处理此类错误。

4. Promise 可以取消吗?

原生 Promise 无法取消,但我们可以使用第三方库(如 abort-controller)来实现 Promise 的取消。

5. 什么时候应该使用 Promise?

Promise 非常适合处理涉及延迟或并行执行的异步操作。它们使我们可以编写可读且可维护的代码,同时仍然保持对异步流程的控制。