手写 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 有所帮助。

相关推荐
爱勇宝8 分钟前
人到了一定年纪,才会看懂这些人性真相
前端·后端
梓䈑26 分钟前
【用 Vibe Coding 实现的 C++17 在线判题系统】前端开发 + Web 自动化测试
前端·c++·ai编程
程序员黑豆1 小时前
鸿蒙应用开发 @BuilderParam 使用教程:实现灵活的 UI 插槽
前端·harmonyos
神奇小汤圆1 小时前
LLM Agent 底层揭秘:大模型如何通过 JSON-RPC 2.0 协议跨进程调工具?
面试
万少1 小时前
用 TraeWork 给小孩做一个家庭工作台
前端·人工智能·后端
孙启超1 小时前
【AI应用开发】ReAct 原理是什么?和普通直接提问 LLM 差别在哪?
前端·人工智能·llm·agent·react·rag·ai应用开发
SynthWriter2 小时前
下班前接到急活?我用 TRAE Work 20 分钟搞定了一套完整页面原型
前端
minimoon_jojo2 小时前
Antd Table虚拟滚动详解
前端
颜进强2 小时前
Calude Code - 23 用 MCP 把 Jenkins 变成 AI 队友:一次对话完成自动化发布
前端·后端
wangjialelele2 小时前
Selenium4 + Java Web自动化测试入门指南:从环境搭建到常用操作详解
java·开发语言·前端·测试工具·自动化