返回

从 PromiseA+ 入门,剖析 JavaScript 中的 Promise 机制

前端

Promise 简介

Promise 是一种 JavaScript 对象,用于表示异步操作的最终完成或失败及其结果。它提供了一种简单而有效的方式来处理异步操作,使代码更易读、更易维护。

Promise 的基本概念

Promise 的状态

Promise 有三种状态:Pending、Fulfilled 和 Rejected。

  • Pending:表示 Promise 尚未完成,异步操作仍在进行中。
  • Fulfilled:表示 Promise 已完成,异步操作成功。
  • Rejected:表示 Promise 已完成,异步操作失败。

Promise 的基本方法

Promise 提供了几个基本方法来处理不同的情况:

  • then:用于在 Promise 完成时执行指定的回调函数。
  • catch:用于在 Promise 失败时执行指定的回调函数。
  • all:用于等待多个 Promise 全部完成,然后执行指定的回调函数。
  • allSettled:用于等待多个 Promise 全部完成,无论成功还是失败,然后执行指定的回调函数。
  • race:用于等待多个 Promise 中的第一个完成,然后执行指定的回调函数。

实现自己的 Promise

要实现自己的 Promise,需要遵循 PromiseA+ 规范。PromiseA+ 规范定义了 Promise 的行为和接口,确保 Promise 在不同的 JavaScript 环境中都能一致地工作。

基本步骤

实现自己的 Promise 可以分为以下几个基本步骤:

  1. 创建 Promise 对象。
  2. 在 Promise 构造函数中传入一个执行器函数。
  3. 在执行器函数中调用 resolve 或 reject 来改变 Promise 的状态。
  4. 使用 then、catch、all 等方法来处理 Promise 的完成或失败。

代码示例

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

    const resolve = (result) => {
      if (this.state !== 'pending') return;

      this.state = 'fulfilled';
      this.result = result;
      this.onFulfilledCallbacks.forEach((callback) => callback(result));
    };

    const reject = (error) => {
      if (this.state !== 'pending') return;

      this.state = 'rejected';
      this.result = error;
      this.onRejectedCallbacks.forEach((callback) => callback(error));
    };

    executor(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      const fulfilledCallback = (result) => {
        if (onFulfilled) {
          try {
            const nextResult = onFulfilled(result);
            resolve(nextResult);
          } catch (error) {
            reject(error);
          }
        } else {
          resolve(result);
        }
      };

      const rejectedCallback = (error) => {
        if (onRejected) {
          try {
            const nextResult = onRejected(error);
            resolve(nextResult);
          } catch (error) {
            reject(error);
          }
        } else {
          reject(error);
        }
      };

      if (this.state === 'fulfilled') {
        setTimeout(() => fulfilledCallback(this.result), 0);
      } else if (this.state === 'rejected') {
        setTimeout(() => rejectedCallback(this.result), 0);
      } else {
        this.onFulfilledCallbacks.push(fulfilledCallback);
        this.onRejectedCallbacks.push(rejectedCallback);
      }
    });
  }

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

  all(promises) {
    return new Promise((resolve, reject) => {
      const results = [];
      let pendingPromises = promises.length;

      promises.forEach((promise, index) => {
        promise
          .then((result) => {
            results[index] = result;
            pendingPromises--;

            if (pendingPromises === 0) {
              resolve(results);
            }
          })
          .catch((error) => {
            reject(error);
          });
      });
    });
  }

  allSettled(promises) {
    return new Promise((resolve, reject) => {
      const results = [];
      let pendingPromises = promises.length;

      promises.forEach((promise, index) => {
        promise
          .then((result) => {
            results[index] = { status: 'fulfilled', result };
            pendingPromises--;

            if (pendingPromises === 0) {
              resolve(results);
            }
          })
          .catch((error) => {
            results[index] = { status: 'rejected', reason: error };
            pendingPromises--;

            if (pendingPromises === 0) {
              resolve(results);
            }
          });
      });
    });
  }

  race(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((promise) => {
        promise
          .then((result) => {
            resolve(result);
          })
          .catch((error) => {
            reject(error);
          });
      });
    });
  }
}

结语

通过剖析 PromiseA+ 规范并实现自己的 Promise,你已经掌握了 Promise 的核心原理和使用方法。掌握 Promise 可以让你更轻松地编写异步代码,并使你的代码更易读、更易维护。在未来的学习和实践中,继续深入探索 Promise 的奥秘,你将发现它在 JavaScript 开发中发挥着越来越重要的作用。