本文深入解析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));
总结
总结
以上即为本文的全部内容,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对本站的支持。