返回

手把手教你手写一个 Promise,一文带你玩转异步编程!

前端

一、为什么会有 Promise 的出现?

在 JavaScript 中,我们经常会遇到异步操作,如网络请求、文件读取等。这些操作需要一段时间才能完成,在等待结果期间,我们需要一种机制来处理后续操作。传统的回调函数虽然可以实现这个目的,但当异步操作嵌套过多时,代码就会变得混乱不堪,难以维护和理解。

为了解决这个问题,Promise 应运而生。它是一种用于处理异步操作的规范,它提供了统一的接口来处理异步操作的成功和失败,使得代码更加清晰和易读。

二、Promise 基础

Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。它有三种状态:

  1. 待定(pending) :初始状态,既没有被兑现(resolved)也没有被拒绝(rejected)。
  2. 兑现(resolved) :异步操作成功完成,Promise 对象的值被设置。
  3. 拒绝(rejected) :异步操作失败,Promise 对象的错误信息被设置。

Promise 对象提供了几个基础方法来处理不同的状态:

  • then() :用于注册回调函数,分别处理兑现和拒绝两种情况。
  • catch() :用于注册回调函数,仅处理拒绝的情况。
  • finally() :无论兑现还是拒绝,都会执行的回调函数。

三、手写一个 Promise

现在,我们来看看如何手写一个 Promise:

class Promise {
  constructor(executor) {
    this.state = "pending"; // 初始状态
    this.value = null; // 值
    this.reason = null; // 错误信息
    this.onFulfilledCallbacks = []; // 兑现回调函数队列
    this.onRejectedCallbacks = []; // 拒绝回调函数队列

    // 执行器函数,负责执行异步操作并设置状态和值
    try {
      executor(
        (value) => {
          this.resolve(value); // 兑现
        },
        (reason) => {
          this.reject(reason); // 拒绝
        }
      );
    } catch (err) {
      this.reject(err); // 如果执行器函数抛出错误,则拒绝
    }
  }

  resolve(value) {
    if (this.state !== "pending") return; // 状态不是待定,直接返回
    this.state = "fulfilled"; // 更改状态为兑现
    this.value = value; // 设置值
    this.onFulfilledCallbacks.forEach((callback) => {
      // 执行兑现回调函数
      callback(value);
    });
  }

  reject(reason) {
    if (this.state !== "pending") return; // 状态不是待定,直接返回
    this.state = "rejected"; // 更改状态为拒绝
    this.reason = reason; // 设置错误信息
    this.onRejectedCallbacks.forEach((callback) => {
      // 执行拒绝回调函数
      callback(reason);
    });
  }

  then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => {
      // 将兑现回调函数添加到队列中
      this.onFulfilledCallbacks.push((value) => {
        try {
          const result = onFulfilled(value);
          // 如果 onFulfilled 返回一个 Promise,则等待其结果
          if (result instanceof Promise) {
            result.then(resolve, reject);
          } else {
            // 否则,直接兑现
            resolve(result);
          }
        } catch (err) {
          // 如果 onFulfilled 抛出错误,则拒绝
          reject(err);
        }
      });

      // 将拒绝回调函数添加到队列中
      this.onRejectedCallbacks.push((reason) => {
        try {
          const result = onRejected(reason);
          // 如果 onRejected 返回一个 Promise,则等待其结果
          if (result instanceof Promise) {
            result.then(resolve, reject);
          } else {
            // 否则,直接兑现
            resolve(result);
          }
        } catch (err) {
          // 如果 onRejected 抛出错误,则拒绝
          reject(err);
        }
      });
    });
  }

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

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

四、Promise 实例

现在,我们可以使用我们的 Promise 来处理异步操作了:

const promise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => {
    // 成功
    resolve("成功!");
  }, 2000);
});

promise.then((value) => {
  // 兑现回调函数
  console.log(value); // "成功!"
}).catch((reason) => {
  // 拒绝回调函数
  console.log(reason); // 不会被执行
});

五、Promise 链式调用

Promise 还支持链式调用,我们可以通过 .then() 方法将多个异步操作串联起来,形成一个异步操作队列:

promise.then((value) => {
  console.log(value); // "成功!"
  return "新的值"; // 返回一个新的值
}).then((value) => {
  console.log(value); // "新的值"
}).catch((reason) => {
  // 不会被执行
});

六、总结

Promise 是 JavaScript 中处理异步操作的利器,它提供了统一的接口来处理异步操作的成功和失败,使得代码更加清晰和易读。通过手写 Promise,我们不仅可以加深对 Promise 的理解,还可以为异步编程打下坚实的基础。

希望这篇博客对您有所帮助,如果您有任何问题或建议,欢迎在评论区留言!