从零手写Promise:理解状态机制与链式调用的核心实现

作者:拾光

时间:2026-10-22

浏览:342

本文深入解析ES6 Promise的内部机制,通过从零实现名为CutePromise的类,详细讲解Promise的三种状态(Pending、Fulfilled、Rejected)及其流转规则。文章涵盖构造函数、resolve/reject逻辑、then方法的链式调用实现,以及如何处理同步与异步场景下的回调执行,帮助开发者彻底掌握Promise核心原理。

文章目录

前言与背景

前言

ECMAScript 作为 JavaScript 语言的国际标准,定义了语言的核心规范,而 JavaScript 则是该标准的具体实现之一。ES6 版本的发布旨在提升 JavaScript 处理大型复杂应用程序的能力,使其能够胜任企业级开发的需求。

概念

在 ES6 规范中,原生提供了 Promise 对象的支持。

Promise 本质上是一个用于传递异步操作消息的对象。它封装了一个未来才会知晓结果的事件(通常是异步操作),并提供了一套统一的 API,以便对异步结果进行后续处理。

思考题与学习目标

三道思考题

在前端开发的初期,处理异步请求通常依赖 callback 回调函数,这种方式简单直接。随着项目复杂度增加,开发者逐渐转向使用 Promise 来处理异步逻辑,因为 Promise 的代码结构更为优雅。然而,由于缺乏对 Promise 内部结构的深入理解,在面对复杂场景时,往往难以灵活运用,调试过程也较为耗时。

因此,本文将引导读者从零开始,手动实现一个具备基本功能的 Promise 库。通过跟随代码编写过程,读者将清晰理解 Promise 的本质及其内部结构,从而在未来处理复杂异步场景时能够更加得心应手。

此外,为了验证读者是否真正掌握了 Promise 的核心机制,文章末尾将提供几道与 Promise 相关的练习题。这些题目并非抽象的理论测试,而是对实际项目中常见场景的抽象,熟练掌握有助于提升前端开发能力。

在此提前列出三道练习题,读者可在阅读正文前尝试在脑海中构思解决方案:

  • 如何实现 Promise 数组的链式调用?
  • 如何对 Promise 进行并发控制?
  • 如何实现 Promise 的异步缓存?

上述三个问题与是否使用 Promise 本身关系不大,但若不能深刻理解 Promise 的运作机制,解决这些问题将颇具挑战。

Promise 核心概念解析

什么是Promise

回到核心内容,Promise 可以被理解为一个容器,其中保存着某个未来才会结束的事件(通常是异步操作)的结果。

根据 ES6 规范,Promise 是一个构造函数,用于生成 Promise 实例。该构造函数接收一个执行函数(executor)作为参数,该函数包含两个参数:resolve 和 reject。实例生成后,可通过 then 方法分别指定状态为 resolved(成功)和 rejected(失败)时的回调函数(onFulfilled 和 onRejected)。

具体使用方式可通过以下代码示例展示:


const promise = new Promise(function(resolve, reject) {
 // ... some code

 if (/* 异步操作成功 */){
 resolve(value);
 } else {
 reject(error);
 }
});

promise.then(function(value) {
 // success
}, function(error) {
 // failure
});

理解上述机制后,我们可以开始构建自己的 Promise 实现,并将其命名为 CutePromise。

CutePromise 实现细节

实现一个Promise:CutePromise

我们将直接使用 ES6 的 class 语法来创建 CutePromise。若对 ES6 语法尚不熟悉,建议先阅读相关 ES6 核心语法介绍文章,如《30分钟掌握ES6/ES2015核心内容(上)》和《30分钟掌握ES6/ES2015核心内容(下)》,然后再回来继续阅读。


class CutePromise {
 // executor是我们实例化CutePromise时传入的参数函数,它接受两个参数,分别是resolve和reject。
 // resolve和reject我们将会定义在constructor当中,供executor在执行的时候调用
 constructor(executor) {
 const resolve = () => {}
 const reject = () => {}
 executor(resolve, reject)
 }

 // 为实例提供一个then的方法,接收两个参数函数,
 // 第一个参数函数必传,它会在promise已成功(fulfilled)以后被调用
 // 第二个参数非必传,它会在promise已失败(rejected)以后被调用
 then(onFulfilled, onRejected) {}
}

创建 CutePromise 后,我们需要明确一个关键点:Promise 对象的状态管理。

Promise 通过其内部状态来控制异步操作的流程。一个 Promise 实例具有三种可能的状态:

  • 异步操作未完成(Pending)
  • 异步操作成功(Fulfilled)
  • 异步操作失败(Rejected)

在上述三种状态中,Fulfilled 和 Rejected 统称为 Resolved(已定型)。状态的切换仅有两条路径:从 Pending 转为 Fulfilled,或从 Pending 转为 Rejected。一旦状态发生切换,便不可再更改。

接下来,我们为 CutePromise 添加状态管理逻辑,大致流程如下:

首先,在实例化初始阶段,将状态设置为 PENDING。随后,当 executor 执行 resolve 时,将状态更改为 FULFILLED;当执行 reject 时,将状态更改为 REJECTED。同时,更新实例的 value 属性以保存结果或错误信息。


constructor(executor) {
 ...
 this.state = 'PENDING';
 ...
 const resolve = (result) => {
  this.state = 'FULFILLED';
  this.value = result;
 }
 const reject = (error) => {
  this.state = 'REJECTED';
  this.value = error;
 }
 ...
}

接下来分析 then 函数。then 接收两个参数:onFulfilled 表示 Promise 异步操作成功时调用的函数,onRejected 表示失败时调用的函数。如果在调用 then 时 Promise 已经完成(例如同步任务),我们可以直接根据实例状态执行相应的回调函数。如果 Promise 状态仍为 PENDING,则将 onFulfilled 和 onRejected 存储到 chained 数组中,待 Promise 执行完毕后再依次调用。


constructor(executor) {
 ...
 this.state = 'PENDING';
 
 // chained用来储存promise执行完成以后,需要被依次调用的一系列函数
 this.chained = [];
 const resolve = (result) => {
  this.state = 'FULFILLED';
  this.value = result;
  
  // promise已经执行成功了,可以依次调用.then()函数里的onFulfilled函数了
  for (const { onFulfilled } of this.chained) {
   onFulfilled(res);
  }
 }

 ...
}
then(onFulfilled, onRejected) {
 if (this.state === 'FULFILLED') {
 onFulfilled(this.value);
 } else if (this.state === 'REJECTED') {
 onRejected(this.value);
 } else {
 this.$chained.push({ onFulfilled, onRejected });
 }
}

至此,我们完成了 CutePromise 的基本创建。以下是完整代码,读者可将代码复制到控制台进行测试:


class CutePromise {
 constructor(executor) {
 if (typeof executor !== 'function') {
  throw new Error('Executor must be a function');
 }

 this.state = 'PENDING';
 this.chained = [];
 const resolve = res => {
  if (this.state !== 'PENDING') {
  return;
  }

  this.state = 'FULFILLED';
  this.internalValue = res;
  for (const { onFulfilled } of this.chained) {
  onFulfilled(res);
  }
 };
 const reject = err => {
  if (this.state !== 'PENDING') {
  return;
  }
  this.state = 'REJECTED';
  this.internalValue = err;
  for (const { onRejected } of this.chained) {
  onRejected(err);
  }
 };

 try {
  executor(resolve, reject);
 } catch (err) {
  reject(err);
 }
 }
 
 then(onFulfilled, onRejected) {
 if (this.state === 'FULFILLED') {
  onFulfilled(this.internalValue);
 } else if (this.$state === 'REJECTED') {
  onRejected(this.internalValue);
 } else {
  this.chained.push({ onFulfilled, onRejected });
 }
 }
}

提供以下测试代码用于验证:


let p = new CutePromise(resolve => {
 setTimeout(() => resolve('Hello'), 100);
});
p.then(res => console.log(res));
p = new CutePromise((resolve, reject) => {
 setTimeout(() => reject(new Error('woops')), 100);
});
p.then(() => {}, err => console.log('Async error:', err.stack));
p = new CutePromise(() => { throw new Error('woops'); });
p.then(() => {}, err => console.log('Sync error:', err.stack));

总结

总结

以上即为本文的全部内容,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对本站的支持。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章