返回

手写Promise,夯实基础,掌握异步编程利器

前端







## 前言

在现代前端开发中,异步编程已成为不可或缺的一部分。随着应用程序变得越来越复杂,我们需要处理越来越多的异步任务,例如网络请求、定时器、事件处理等等。为了更好地管理和协调这些异步任务,JavaScript 引入了 Promise 对象。

Promise 是一个表示异步操作及其最终结果的 JavaScript 对象。它提供了一种统一、简单的方式来处理异步操作,使得代码更易读、更易维护。

## 手写 Promise

为了深入理解 Promise 的工作原理,我们不妨尝试自己手写一个 Promise。

```javascript
class Promise {
  constructor(executor) {
    this.state = 'pending'; // 初始状态为 pending
    this.value = undefined; // 最终值
    this.reason = undefined; // 拒绝原因
    this.onFulfilledCallbacks = []; // 保存成功的回调函数
    this.onRejectedCallbacks = []; // 保存失败的回调函数

    // 执行器函数立即执行,传入 resolve 和 reject 函数
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    // 规范要求,如果 onFulfilled 或 onRejected 不是函数,则直接忽略
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
    onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason; };

    // 返回一个新的 Promise 对象
    return new Promise((resolve, reject) => {
      // 将成功回调和失败回调添加到相应的数组中
      this.onFulfilledCallbacks.push(() => {
        // 将成功回调执行结果传递给下一个 Promise 的 resolve 函数
        resolve(onFulfilled(this.value));
      });

      this.onRejectedCallbacks.push(() => {
        // 将失败回调执行结果传递给下一个 Promise 的 reject 函数
        reject(onRejected(this.reason));
      });

      // 如果 Promise 状态已经确定,则立即执行回调
      if (this.state === 'fulfilled') {
        this.onFulfilledCallbacks[0]();
      } else if (this.state === 'rejected') {
        this.onRejectedCallbacks[0]();
      }
    });
  }

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

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

  resolve(value) {
    if (this.state !== 'pending') {
      return;
    }

    this.state = 'fulfilled';
    this.value = value;

    // 执行成功回调
    this.onFulfilledCallbacks.forEach((callback) => {
      callback();
    });
  }

  reject(reason) {
    if (this.state !== 'pending') {
      return;
    }

    this.state = 'rejected';
    this.reason = reason;

    // 执行失败回调
    this.onRejectedCallbacks.forEach((callback) => {
      callback();
    });
  }
}

Promise 的工作原理

从手写 Promise 的代码中,我们可以看到 Promise 的工作原理大致如下:

  1. Promise 构造函数接受一个 executor 函数作为参数,executor 函数立即执行,传入 resolve 和 reject 函数。
  2. executor 函数中,通过调用 resolve 函数来将 Promise 的状态从 pending 更改为 fulfilled,并将结果值作为参数传递给 resolve 函数。
  3. executor 函数中,通过调用 reject 函数来将 Promise 的状态从 pending 更改为 rejected,并将错误原因作为参数传递给 reject 函数。
  4. then 方法用于在 Promise 状态改变时添加回调函数。当 Promise 状态为 fulfilled 时,会执行成功的回调函数;当 Promise 状态为 rejected 时,会执行失败的回调函数。
  5. catch 方法用于处理 Promise 的失败情况。它相当于 then 方法的简写形式,只有失败的回调函数。
  6. finally 方法用于在 Promise 状态改变后执行的回调函数,无论 Promise 是成功还是失败。

Promise 的使用

在实际开发中,我们可以直接使用 JavaScript 内置的 Promise 对象,而无需自己手写 Promise。Promise 的使用非常简单,只需要按照以下步骤即可:

  1. 创建一个 Promise 对象。
  2. 在 Promise 对象中,使用 then 方法添加成功和失败的回调函数。
  3. 在 Promise 对象中,使用 resolve 函数来将 Promise 的状态从 pending 更改为 fulfilled,并将结果值作为参数传递给 resolve 函数。
  4. 在 Promise 对象中,使用 reject 函数来将 Promise 的状态从 pending 更改为 rejected,并将错误原因作为参数传递给 reject 函数。
const promise = new Promise((resolve, reject) => {
  // 异步操作
  setTimeout(() => {
    resolve('成功结果');
  }, 1000);
});

promise.then(
  (result) => {
    console.log(result); // 输出:成功结果
  },
  (error) => {
    console.log(error); // 不会执行
  }
);

Promise 的注意事项

在使用 Promise 时,需要注意以下几点:

  1. Promise 对象一旦状态改变,就无法再改变。
  2. then 方法和 catch 方法都返回一个新的 Promise 对象。
  3. then 方法可以多次调用,每次调用都会添加一个新的回调函数。
  4. catch 方法只能调用一次,如果多次调用,只有第一次调用的回调函数会被执行。
  5. finally 方法总是会被执行,无论 Promise 是成功还是失败。

结语

Promise 是 JavaScript 中处理异步编程的利器,它使得代码更易读、更易维护。通过手写 Promise,我们可以深入理解 Promise 的工作原理和使用方法。在实际开发中,我们可以直接使用 JavaScript 内置的 Promise 对象,并遵循 Promise 的注意事项,以确保代码的正确性和健壮性。