返回

Promise 手写实现:掌握 Promise A+ 规范的 JavaScript 实战

前端

前言

在现代 Web 开发中,异步编程已经成为常态。它允许我们在不阻塞主线程的情况下执行耗时操作,从而提升用户体验。然而,传统的回调方式往往会造成代码冗余和难以维护的“回调地狱”。

Promise 是一种解决此问题的优雅解决方案。它是一种用于处理异步操作的规范化机制,提供了一种更简洁、更易于管理的方式来编写异步代码。

本文将带领你从头开始,用 JavaScript 手写一个符合 Promise A+ 规范的 Promise 实现。我们将深入解析 Promise 的核心概念,包括状态管理、异常处理和回调处理,并通过编写实际示例来巩固你的理解。准备好踏上异步编程的进阶之旅了吗?

Promise A+ 规范

Promise A+ 规范是一套标准,定义了 Promise 的行为。它确保了不同实现之间的兼容性和一致性。了解 Promise A+ 规范对于编写符合规范的 Promise 实现至关重要。

Promise A+ 规范主要包含以下几个要点:

  • Promise 有三种状态:Pending、Fulfilled 和 Rejected。
  • 每个 Promise 只能从 Pending 状态转换到 Fulfilled 或 Rejected 状态,且状态一旦确定就不能更改。
  • Promise 接受一个回调函数作为参数,该函数将接收 Promise 的最终值或拒绝原因。
  • Promise 的 then() 方法允许你附加回调函数,以便在 Promise 完成后处理结果或拒绝原因。
  • then() 方法返回一个新的 Promise,该 Promise 的状态取决于原始 Promise 的状态。
  • Promise 可以被链式调用,形成 Promise 链。
  • Promise 必须处理异步操作和同步异常。

手写 Promise 源码

让我们开始动手编写一个符合 Promise A+ 规范的 Promise 实现吧!

class Promise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    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 (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    const promise = new Promise();

    if (this.state === 'fulfilled') {
      setTimeout(() => {
        try {
          const result = onFulfilled(this.value);
          resolvePromise(promise, result);
        } catch (error) {
          rejectPromise(promise, error);
        }
      }, 0);
    } else if (this.state === 'rejected') {
      setTimeout(() => {
        try {
          const result = onRejected(this.reason);
          resolvePromise(promise, result);
        } catch (error) {
          rejectPromise(promise, error);
        }
      }, 0);
    } else {
      this.onFulfilledCallbacks.push(() => {
        try {
          const result = onFulfilled(this.value);
          resolvePromise(promise, result);
        } catch (error) {
          rejectPromise(promise, error);
        }
      });
      this.onRejectedCallbacks.push(() => {
        try {
          const result = onRejected(this.reason);
          resolvePromise(promise, result);
        } catch (error) {
          rejectPromise(promise, error);
        }
      });
    }

    return promise;
  }
}

const resolvePromise = (promise, result) => {
  if (result instanceof Promise) {
    result.then(
      (value) => resolvePromise(promise, value),
      (reason) => rejectPromise(promise, reason)
    );
  } else {
    promise.state = 'fulfilled';
    promise.value = result;
    promise.onFulfilledCallbacks.forEach((callback) => callback(result));
  }
};

const rejectPromise = (promise, reason) => {
  promise.state = 'rejected';
  promise.reason = reason;
  promise.onRejectedCallbacks.forEach((callback) => callback(reason));
};

核心概念详解

状态管理

Promise 有三种状态:Pending、Fulfilled 和 Rejected。在构造函数中,Promise 的状态被初始化为 Pending。当 Promise 被 resolve 时,状态变为 Fulfilled,并存储一个结果值。当 Promise 被 reject 时,状态变为 Rejected,并存储一个拒绝原因。

回调处理

then() 方法允许你附加回调函数,以便在 Promise 完成后处理结果或拒绝原因。回调函数接收一个参数,该参数是 Promise 的结果值或拒绝原因。

异常处理

Promise 可以处理异步操作和同步异常。如果 Promise 的执行器函数抛出异常,Promise 将被 reject 并存储该异常。同样,如果 then() 方法中的回调函数抛出异常,新的 Promise 将被 reject 并存储该异常。

链式调用

Promise 可以被链式调用,形成 Promise 链。这使得你可以轻松地处理一系列异步操作,而无需嵌套回调函数。

实际示例

让我们通过一个实际示例来巩固我们的理解。假设我们有一个函数,该函数从服务器获取数据。我们可以使用 Promise 来处理此异步操作,如下所示:

function getData() {
  return new Promise((resolve, reject) => {
    // 发起异步请求
    $.ajax({
      url: 'api/data',
      success: (data) => resolve(data),
      error: (error) => reject(error)
    });
  });
}

getData().then((data) => {
  // 处理成功的结果
}, (error) => {
  // 处理拒绝的原因
});

总结

本文带你从零开始,用 JavaScript 手写了一个符合 Promise A+ 规范的 Promise 实现。通过深入解析 Promise 的核心概念,包括状态管理、异常处理和回调处理,我们了解了 Promise 如何让异步编程变得更加简单和高效。

掌握 Promise 的原理和用法对于现代 Web 开发至关重要。它可以帮助你摆脱回调地狱,编写更具可读性、可维护性和可扩展性的异步代码。现在,你已经装备好知识和实践技能,可以自信地拥抱 Promise 的强大功能,并将你的异步编程技巧提升到一个新的高度!