返回

手把手教你实现CustomPromise,理解Promise实践应用

前端

前言

在前端开发中,我们经常会遇到异步操作,如网络请求、定时器等。这些操作的特点是无法立即得到结果,需要等待一段时间才能获取结果。为了处理异步操作,传统的做法是使用回调函数。然而,当异步操作嵌套较多时,代码就会变得难以阅读和维护,这种现象被称为“回调地狱”。

Promise对象是JavaScript中用于处理异步操作的解决方案。它提供了一种更简洁、更易读的方式来处理异步操作。通过使用Promise对象,我们可以将异步操作包装成一个Promise对象,并使用.then()方法来指定异步操作成功时的回调函数,使用.catch()方法来指定异步操作失败时的回调函数。这样,我们就不用再使用嵌套回调函数来处理异步操作了。

Promise的基本原理

Promise对象本质上是一个容器,它可以存储一个值或一个错误。当Promise对象被创建时,它处于“pending”状态。当异步操作成功时,Promise对象的状态会变为“fulfilled”,并存储异步操作的结果。当异步操作失败时,Promise对象的状态会变为“rejected”,并存储异步操作的错误信息。

当Promise对象处于“pending”状态时,我们可以使用.then()方法来指定异步操作成功时的回调函数。当Promise对象处于“fulfilled”状态时,.then()方法指定的回调函数就会被调用,并将异步操作的结果作为参数传递给回调函数。当Promise对象处于“rejected”状态时,.catch()方法指定的回调函数就会被调用,并将异步操作的错误信息作为参数传递给回调函数。

Promise的使用场景

Promise对象在前端开发中有着广泛的应用场景,包括:

  • 网络请求:我们可以使用Promise对象来包装网络请求,并使用.then()方法来指定网络请求成功时的回调函数。这样,我们就不用再使用回调函数来处理网络请求了。
  • 定时器:我们可以使用Promise对象来包装定时器,并使用.then()方法来指定定时器执行完成时的回调函数。这样,我们就不用再使用回调函数来处理定时器了。
  • 并发操作:我们可以使用Promise对象来处理并发操作。例如,我们可以使用Promise.all()方法来等待多个异步操作同时完成,并使用.then()方法来指定所有异步操作完成时的回调函数。

CustomPromise的实现步骤

接下来,我们将通过一点点的实现一个自己的CustomPromise。为了便于理解,我们将把CustomPromise的实现分为以下几个步骤:

  1. 创建CustomPromise类
  2. 实现CustomPromise类的构造函数
  3. 实现CustomPromise类的.then()方法
  4. 实现CustomPromise类的.catch()方法
  5. 实现CustomPromise类的.finally()方法

创建CustomPromise类

首先,我们需要创建一个CustomPromise类。我们可以使用class来创建CustomPromise类。

class CustomPromise {
  // ...
}

实现CustomPromise类的构造函数

在CustomPromise类的构造函数中,我们需要做两件事:

  1. 初始化Promise对象的状态,将其设置为“pending”。
  2. 接收一个回调函数作为参数,该回调函数将异步操作的成功回调函数和失败回调函数作为参数。
class CustomPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.error = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  // ...
}

实现CustomPromise类的.then()方法

在CustomPromise类的.then()方法中,我们需要做两件事:

  1. 将成功回调函数和失败回调函数添加到相应的回调函数数组中。
  2. 如果Promise对象的状态已经变为“fulfilled”或“rejected”,则立即调用相应的回调函数。
class CustomPromise {
  // ...

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

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

      if (this.state === 'fulfilled') {
        this.onFulfilledCallbacks[0]();
      } else if (this.state === 'rejected') {
        this.onRejectedCallbacks[0]();
      }
    });
  }

  // ...
}

实现CustomPromise类的.catch()方法

在CustomPromise类的.catch()方法中,我们需要做两件事:

  1. 将失败回调函数添加到失败回调函数数组中。
  2. 如果Promise对象的状态已经变为“rejected”,则立即调用失败回调函数。
class CustomPromise {
  // ...

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

  // ...
}

实现CustomPromise类的.finally()方法

在CustomPromise类的.finally()方法中,我们需要做两件事:

  1. 将一个回调函数添加到回调函数数组中,该回调函数将在Promise对象的状态变为“fulfilled”或“rejected”时被调用。
  2. 返回一个新的Promise对象,该Promise对象的状态与当前Promise对象的状态相同。
class CustomPromise {
  // ...

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

  // ...
}

CustomPromise与原生Promise的对比

CustomPromise与原生Promise最大的区别在于CustomPromise是由我们自己实现的,而原生Promise是由JavaScript引擎实现的。这意味着CustomPromise在某些方面可能不如原生Promise那么完善,但它也具有原生Promise不具备的一些优势。

CustomPromise的优势

  • 可定制性: CustomPromise的实现方式可以根据我们的具体需求进行定制。例如,我们可以根据需要添加额外的属性或方法到CustomPromise类中。
  • 可扩展性: CustomPromise的实现方式可以根据需要进行扩展。例如,我们可以添加对并发操作的支持到CustomPromise类中。

CustomPromise的劣势

  • 性能: CustomPromise的性能可能不如原生Promise那么好。这是因为CustomPromise是由我们自己实现的,而原生Promise是由JavaScript引擎实现的。
  • 兼容性: CustomPromise可能不兼容所有浏览器。这是因为CustomPromise是由我们自己实现的,而原生Promise是JavaScript标准的一部分。

结语

通过本文,我们学习了如何实现一个自己的CustomPromise。CustomPromise可以帮助我们更轻松地处理异步操作,并避免“回调地狱”的出现。在实际开发中,我们可以根据需要选择使用CustomPromise或原生Promise。