返回

手写 Promise 从入门到掌握进阶版

前端

从零开始手写 Promise

在现代 JavaScript 开发中,处理异步操作至关重要。Promise 应运而生,为我们提供了一种优雅而强大的解决方案,让我们能够以可控且可预测的方式处理异步代码。本文将深入探讨 Promise 的内部原理,引导你逐步实现一个符合 Promise A+ 规范的手写 Promise。

什么是 Promise?

Promise 是一种对象,它代表着一个异步操作的最终结果,无论是成功还是失败。当异步操作完成后,Promise 会被标记为已完成或已拒绝,并提供一个结果值或一个错误原因。

手写一个简单的 Promise

让我们从一个基本 Promise 实现开始。这是一个简化的版本,重点在于理解核心概念:

class Promise {
  constructor(executor) {
    this.state = "pending";
    this.value = null;
    this.error = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    executor(this.resolve.bind(this), this.reject.bind(this));
  }

  resolve(value) {
    if (this.state === "pending") {
      this.state = "fulfilled";
      this.value = value;
      this.onFulfilledCallbacks.forEach(callback => callback(value));
    }
  }

  reject(error) {
    if (this.state === "pending") {
      this.state = "rejected";
      this.error = error;
      this.onRejectedCallbacks.forEach(callback => callback(error));
    }
  }

  then(onFulfilled, onRejected) {
    if (this.state === "fulfilled") {
      onFulfilled(this.value);
    } else if (this.state === "rejected") {
      onRejected(this.error);
    } else {
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected);
    }

    return new Promise((resolve, reject) => {
      this.then(value => {
        try {
          const result = onFulfilled(value);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, error => {
        try {
          const result = onRejected(error);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      });
    });
  }

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

这个 Promise 实现的基本原理如下:

  • executor 函数:它接受两个参数:一个用于解析 Promise 的函数 resolve 和一个用于拒绝 Promise 的函数 reject
  • 状态 :Promise 有三种可能的状态:pending(等待)、fulfilled(已完成)和 rejected(已拒绝)。
  • 结果和错误 :当 Promise 完成时,它会存储一个结果值或一个错误原因。
  • 回调队列onFulfilledCallbacksonRejectedCallbacks 队列存储着在 Promise 完成或拒绝时要调用的回调函数。
  • 链式调用then() 方法允许你链式调用多个 Promise,这样可以优雅地处理异步操作序列。

符合 Promise A+ 规范的 Promise

Promise A+ 规范定义了一组规则,以确保不同 Promise 实现之间的一致性。为了符合规范,我们的 Promise 实现需要一些修改:

  • 处理特殊情况,例如 executor 函数抛出错误或多次调用 resolvereject
  • 实现 Promise 链式调用,即 then() 方法返回的 Promise 能够继续使用 then() 方法进行链式调用。
  • 实现静态方法,例如 Promise.all(), Promise.race(), Promise.resolve()Promise.reject()

Promise 的进阶应用

一旦掌握了 Promise 的基础知识,就可以探索其更高级的特性:

  • Promise.all() :将多个 Promise 包装成一个新的 Promise,并等待所有 Promise 完成。
  • Promise.race() :将多个 Promise 包装成一个新的 Promise,并等待第一个 Promise 完成或拒绝。
  • Promise.resolve() :将一个值包装成一个已完成的 Promise。
  • Promise.reject() :将一个错误包装成一个已拒绝的 Promise。

总结

通过手写 Promise,我们不仅深入了解了它的内部原理,还为我们提供了在 JavaScript 开发中有效利用 Promise 的工具。Promise 的强大功能在于它提供了对异步操作的结构化和可控处理方式,使编写更健壮、更可维护的代码成为可能。

常见问题解答

  1. 为什么使用 Promise 而不用回调函数?
    Promise 提供了更优雅和可维护的方式来处理异步操作。它消除了回调函数的嵌套,使代码更易于阅读和理解。

  2. 什么时候应该使用 then()catch()
    then() 用于处理 Promise 完成时的结果,而 catch() 用于处理 Promise 拒绝时的错误。

  3. 如何处理 Promise 链式调用中的错误?
    每个 then() 方法都返回一个新的 Promise,因此可以在链式调用的任何点使用 catch() 来处理错误。

  4. Promise.all() 和 Promise.race() 有什么区别?
    Promise.all() 等待所有 Promise 完成,而 Promise.race() 等待第一个 Promise 完成或拒绝。

  5. 如何使用 Promise 进行并行异步操作?
    可以使用 Promise.all() 将多个异步操作包装成一个 Promise,并等待所有操作完成。