手撕 Promise:深入理解 JavaScript 异步编程

45 阅读2分钟

手撕 Promise:深入理解 JavaScript 异步编程

引言

JavaScript 的异步编程是前端开发中不可或缺的一部分。Promise 是处理异步操作的核心机制之一。理解 Promise 的内部实现可以帮助我们更好地掌握异步编程的精髓。本文将带你一步步实现一个简单的 Promise。

Promise 的基础

在开始手撕 Promise 之前,我们需要了解 Promise 的基本行为:

  • Promise 有三种状态:Pending(等待中)、Fulfilled(已成功)、Rejected(已失败)。
  • 一旦状态改变,它就成为不可变的。
  • Promise 可以链式调用。

手动实现 Promise

定义 Promise 类

首先,我们定义一个 Promise 类,并初始化状态为 Pending。

class MyPromise {
  constructor(executor) {
    // 初始化状态为 Pending
    this.state = 'pending';
    this.value = undefined;
    this.handlers = [];

    try {
      executor(this._resolve, this._reject);
    } catch (err) {
        this._reject(err);
      }
  }

  _resolve = (value) => {
    // 状态转换为 Fulfilled
    if (this.state === 'pending') {
      this.state = 'fulfilled';
      this.value = value;
      this.handlers.forEach((h) => this._handle(h));
    }
  }

  _reject = (error) => {
    // 状态转换为 Rejected
    if (this.state === 'pending') {
      this.state = 'rejected';
      this.value = error;
      this.handlers.forEach((h) => this._handle(h));
    }
  }

  _handle(handler) {
    if (this.state === 'fulfilled' && typeof handler.onFulfilled === 'function') {
      handler.onFulfilled(this.value);
    }
    if (this.state === 'rejected' && typeof handler.onRejected === 'function') {
      handler.onRejected(this.value);
    }
  }

  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this.handlers.push({
        onFulfilled: value => {
          if (onFulfilled) {
            try {
              resolve(onFulfilled(value));
            } catch (err) {
              reject(err);
            }
          } else {
            resolve(value);
          }
        },
        onRejected: error => {
          if (onRejected) {
            try {
              resolve(onRejected(error));
            } catch (err) {
              reject(err);
            }
          } else {
            reject(error);
          }
        }
      });
    });
  }

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

使用自定义 Promise

现在我们可以使用这个自定义的 Promise 来处理异步操作。

const asyncTask = (time) => new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(`Task completed after ${time}ms`);
  }, time);
});

asyncTask(1000).then(result => {
  console.log(result); // Task completed after 1000ms
}).catch(error => {
  console.error(error);
});

错误处理

我们的 Promise 实现也支持错误处理,如果 executor 函数抛出异常,或者 onFulfilledonRejected 回调函数抛出异常,它们会被正确捕获并处理。

结论

通过手动实现 Promise,我们可以更深入地理解其内部机制和行为。这不仅是一种很好的学习方式,也是提高编程技能的有效途径。虽然在实际开发中我们很少需要手动实现 Promise,但这样的练习无疑加深了我们对异步编程的理解。