手写 Promise:深入理解异步编程

手写 Promise:深入理解异步编程

在现代 JavaScript 中,Promise 是一种用于处理异步操作的重要工具。通过手写实现一个简单的 Promise 类,我们能更深入地理解 Promise 的工作原理和异步编程的机制。在这篇博客中,我们将逐步构建一个功能齐全的 Promise 类,包括基本结构、then 方法、静态方法如 resolverejectallallSettledraceany,以及一些辅助方法。

Promise 的基本结构

首先,我们定义一个 MyPromise 类,其中包含常见的 Promise 状态和基本结构:

javascript 复制代码
class MyPromise {
  static REJECTED = 'rejected';
  static PENDING = 'pending';
  static FULFILLED = 'fulfilled';

  value = undefined;
  status = MyPromise.PENDING;
  onFulfilledCallBacks = [];
  onRejectedCallBacks = [];

  constructor(execute) {
    const resolve = (value) => {
      if (this.status === MyPromise.PENDING) {
        this.value = value;
        this.status = MyPromise.FULFILLED;
        this.onFulfilledCallBacks.forEach((func) => func(value));
      }
    };

    const reject = (value) => {
      if (this.status === MyPromise.PENDING) {
        this.value = value;
        this.status = MyPromise.REJECTED;
        this.onRejectedCallBacks.forEach((func) => func(value));
      }
    };

    try {
      execute(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  // ...(后续实现)
}

这里定义了 MyPromise 类的基本结构,包括状态常量、初始值、状态和执行回调数组等。

实现 then 方法

then 方法是 Promise 的核心,它用于注册在 Promise 完成时执行的回调函数。以下是 then 方法的基本实现:

javascript 复制代码
then(onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (onFulfilled) => onFulfilled;
  onRejected = typeof onRejected === 'function' ? onRejected : (onRejected) => onRejected;

  return new MyPromise((resolve, reject) => {
    // 处理已完成状态
    if (this.status === MyPromise.FULFILLED) {
      try {
        queueMicrotask(() => {
          const result = onFulfilled(this.value);
          this.handlePromiseResult(result, resolve, reject);
        });
      } catch (error) {
        reject(error);
      }
    }
    // 处理已拒绝状态
    else if (this.status === MyPromise.REJECTED) {
      try {
        queueMicrotask(() => {
          const result = onRejected(this.value);
          this.handlePromiseResult(result, resolve, reject);
        });
      } catch (error) {
        reject(error);
      }
    }
    // 处理异步状态
    else {
      this.onFulfilledCallBacks.push((value) => {
        queueMicrotask(() => {
          const result = onFulfilled(value);
          this.handlePromiseResult(result, resolve, reject);
        });
      });
      this.onRejectedCallBacks.push((value) => {
        queueMicrotask(() => {
          const result = onRejected(value);
          this.handlePromiseResult(result, resolve, reject);
        });
      });
    }
  });
}

handlePromiseResult(result, resolve, reject) {
  if (result instanceof MyPromise) {
    result.then(resolve, reject);
  } else {
    resolve(result);
  }
}

then 方法中,我们判断当前 Promise 的状态,根据状态的不同执行相应的回调函数。如果是异步状态,则将回调函数推入对应的回调数组中,等待状态改变时执行。

实现 resolvereject 静态方法

resolverejectMyPromise 类的两个静态方法,用于创建已解决或已拒绝的 Promise。以下是它们的实现:

javascript 复制代码
static resolve = (value) => {
  return new MyPromise((resolve, reject) => {
    resolve(value);
  });
};

static reject = (value) => {
  return new MyPromise((resolve, reject) => {
    reject(value);
  });
};

这两个方法分别返回一个已解决或已拒绝的 Promise。

all 方法

all 方法接收一个 Promise 数组,返回一个新的 Promise。只有当所有输入的 Promise 都解决时,新 Promise 才会解决,并返回包含所有 Promise 结果的数组;否则,只要有一个 Promise 拒绝,新 Promise 就会被拒绝。

javascript 复制代码
static all = (promises) => {
  if (!this._hasIterator(promises)) {
    throw new Error('参数不可迭代');
  }

  return new MyPromise((resolve, reject) => {
    const resultArr = [];
    promises.forEach((promise) => {
      promise.then(
        (res) => {
          if (resultArr.length === promises.length) {
            resolve(resultArr);
          }
          resultArr.push(res);
        },
        (err) => {
          reject(err);
        }
      );
    });
  });
};

实现 allSettledraceany 方法

allSettled 方法返回一个 Promise,该 Promise 在所有给定的 Promise 都已经解决或拒绝后解决。它会等待所有的 Promise 完成,无论成功还是失败,并返回一个包含每个 Promise 结果的数组。

javascript 复制代码
static allSettled = (promises) => {
  if (!this._hasIterator(promises)) {
    throw new Error('参数不可迭代');
  }

  return new MyPromise((resolve) => {
    const resultArr = [];
    promises.forEach((promise, index) => {
      promise.then(
        (res) => {
          resultArr.push({ status: 'fulfilled', value: res });
          if (resultArr.length === promises.length) {
            resolve(resultArr);
          }
        },
        (res) => {
          resultArr.push({ status: 'rejected', reason: res });
          if (resultArr.length === promises.length) {
            resolve(resultArr);
          }
        }
      );
    });
  });
};

race 方法返回一个 Promise,该 Promise 在给定的任意 Promise 解决或拒绝后立即解决。

javascript 复制代码
static race = (promises) => {
  if (!this._hasIterator(promises)) {
    throw new Error('参数不可迭代');
  }

  return new MyPromise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(
        (res) => {
          resolve(res);
        },
        (res) => {
          reject(res);
        }
      );
    });
  });
};

any 方法返回一个 Promise,该 Promise 在给定的任意 Promise 解决后立即解决。与 race 不同的是,any 只要有一个 Promise 解决即可,不管其状态是成功还是失败。

javascript 复制代码
static any = (promises) => {
  if (!this._hasIterator(promises)) {
    throw new Error('参数不可迭代');
  }

  return new MyPromise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then((res) => {
        resolve(res);
      });
    });
  });
};

辅助方法:deferred

为了方便创建一个延迟解决的 Promise 对象,我们添加了 deferred 方法:

javascript 复制代码
static deferred = () => {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};

这个方法返回一个包含 promiseresolvereject 的对象,方便后续使用。

总结

通过以上的实现,我们成功地创建了一个简单但功能齐全的 Promise 类。这个手写 Promise 不仅具备基本的异步操作能力,还支持 Promise 链式调用和常见的 Promise 方法,如 thenresolverejectallallSettledraceany

手写实现 Promise 是一个有挑战性但也有益处的练习,有助于更深入地理解 Promise 的工作原理和异步编程的机制。希望这篇博客对你理解 Promise 有所帮助。

相关推荐
李剑一1 分钟前
前端使用Web Cryptography API进行内容加密,几乎无法破解
前端
搬砖码1 分钟前
优雅实现!自定义滚动刻度选择器,精准选择无压力
前端
盏茶作酒292 分钟前
打造自己的组件库(二)CSS工程化方案
前端
mwq301234 分钟前
AI编程工具:Claude Code安装和使用
前端·claude
gnip19 分钟前
项目开发流程之技术调用流程
前端·javascript
转转技术团队33 分钟前
多代理混战?用 PAC(Proxy Auto-Config) 优雅切换代理场景
前端·后端·面试
南囝coding34 分钟前
这几个 Vibe Coding 经验,真的建议学!
前端·后端
gnip1 小时前
SSE技术介绍
前端·javascript
掘金安东尼1 小时前
蔚来 600 亿研发成本,信还是不信。。
面试·程序员·github
yinke小琪1 小时前
JavaScript DOM节点操作(增删改)常用方法
前端·javascript