写这篇文章的目的是解剖Promise源码,起因也是最近秋招被问到了让手写Promise,另外在网上看到的Promise源码或多或少有些小问题,也就是没有完全遵循Promise/A+规范。
代码会完全使用ES6
语法,主要分以下几个模块:
- 整体分析(为代码书写铺路)
- 实现初版(构造函数大致功能亿完善)
- 支持异步和链式调用(完善then方法)
- 实现catch方法
- 实现Promise.resolve()
- 实现Promise.reject()
- 实现Promise.all()
- 实现Promise.race()
一、整体分析
所谓Promise就是一个容器,有三个状态:PENDING(进行中)、FULFILLED(成功)、REJECTED(失败),里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,有两大特点:
- 容器状态不受外界影响
- 一旦状态改变就不会再变,任何时候都可以得到这个结果
来看下Promise的用法:
new Promise((resolve, reject) => {
// ...
// 成功则执行resolve,否则指定reject
}).then(
res => {
// resolve对应触发函数的执行
},
err => {
// reject对应触发函数的执行
}
).then(
// 支持链式调用
res => {
}
).catch(
err => console.log(err)
)
Promise.resolve();
Promise.reject();
Promise.all([promise1, promise2, ...]).then();
Promise.race([promise1, promise2, ...]).then();
通过用法不难分析出:
- Promise构造函数接受一个函数参数exector,exector接受resolve和reject两个函数并立即执行,通过resolve/reject改变状态
- 状态改变后,触发原型链上的
then、catch
方法 - Promise类拥有静态方法
resolve、reject、all、race
那么可以写出大致结构代码:
class Promise {
constructor(exector) {
const resolve = () => {
}
const reject = () => {
}
exector(resolve, reject);
}
then() {
}
catch() {
}
static resolve() {
}
static reject() {
}
static all() {
}
static race() {
}
}
之后在此基础上补充代码。
二、实现初版
首先引入三种状态,完善resolve
、reject
函数,最后在构造函数内执行exector(resolve, reject)
:
// 定义三种状态
const PENDING = 'PENDING'; // 进行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失败
class Promise {
constructor(exector) {
// 初始化状态
this.status = PENDING;
// 将成功、失败结果放在this上,便于then、catch访问
this.value = undefined;
this.reason = undefined;
const resolve = value => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
const reject = reason => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
// 立即执行exector
// 把内部的resolve和reject传入executor,用户可调用resolve和reject
exector(resolve, reject);
}
}
注意:exector(resolve, reject);
执行可能会报错,所以需要使用try
包括一下,有报错reject
抛出去。
constructor(exector) {
// 初始化状态
this.status = PENDING;
// 将成功、失败结果放在this上,便于then、catch访问
this.value = undefined;
this.reason = undefined;
const resolve = value => {
if (this.status === PENDING) {
// 只有进行中状态才能更改状态
this.status = FULFILLED;
this.value = value;
}
}
const reject = reason => {
if (this.status === PENDING) {
// 只有进行中状态才能更改状态
this.status = REJECTED;
this.reason = reason;
}
}
// 修改代码
try {
// 立即执行executor
// 把内部的resolve和reject传入executor,用户可调用resolve和reject
exector(resolve, reject);
} catch(e) {
// executor执行出错,将错误内容reject抛出去
reject(e);
}
}
此时可以使用then
进行捕获了,then
接收两个函数,分别对应FULFILLED
和REJECTED
状态:
new Promise().then(
res => {},
err => {},
)
注意:then
、catch
是微任务,这里使用setTimeout模拟:
then(onFulfilled, onRejected) {
// then是微任务,这里用setTimeout模拟
setTimeout(() => {
if (this.status === FULFILLED) {
// FULFILLED状态下才执行
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// REJECTED状态下才执行
onRejected(this.reason);
}
})
}
OK,初版已经完成:
// 定义三种状态
const PENDING = 'PENDING'; // 进行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失败
class Promise {
constructor(exector) {
// 初始化状态
this.status = PENDING;
// 将成功、失败结果放在this上,便于then、catch访问
this.value = undefined;
this.reason = undefined;
const resolve = value => {
if (this.status === PENDING) {
// 只有进行中状态才能更改状态
this.status = FULFILLED;
this.value = value;
}
}
const reject = reason => {
if (this.status === PENDING) {
// 只有进行中状态才能更改状态
this.status = REJECTED;
this.reason = reason;
}
}
try {