这次来搞一篇 promise 规范和基于规范手撕 Promise 的文章

首先进行 3W 常规操作:什么是 Promise?Promise 有什么用?怎样用 Promise?

什么是 Promise

Promise 表示一个异步操作的最终结果,它有三种状态,分别是:

  1. 等待态(pending)
  2. 执行态(resolved)
  3. 拒绝态(rejected)

其初始状态为 pending,在整个异步过程中,他只能处于两种状态,且一旦状态变更为另一种就不再变化

如 pending-> resolved / pending->rejected

Talk is cheap,show u the code

1
2
3
4
5
new Promise((resolve, reject) => {
resolve("success"); //调用resolve函数使得Promise状态变更为resolved

reject("fail"); //此处无效 (两种状态)
});

需要注意的是:在使用 new 构造 Promise 的时候,构造函数内部的代码会立即执行

1
2
3
4
5
6
new Promise((resolve, reject) => {
console.log("first");
});
console.log("second");
//first
//second

上面这段函数执行的结果先打印first 后打印second

简而言之,Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因。

对于 Promise 要有更深的理解,就得知道它是怎么来的,接下来讲一下 Promise A+规范,以对其有一个全面的认识。

Promise A+ 规范

  • 解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后续的 promise 实现多以 resolve 来指代之。
  • 拒绝(reject):指一个 promise 失败时进行的一系列操作。
  • 终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
  • 拒因(error):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。

Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的终值或本 promise 不能执行的原因。

要求:

Promise 的状态

一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)执行态(Fulfilled)拒绝态(Rejected)

等待态(Pending)

处于等待态时,promise 需满足以下条件:

  • 可以迁移至执行态或拒绝态

执行态(Fulfilled)

处于执行态时,promise 需满足以下条件:

  • 不能迁移至其他任何状态
  • 必须拥有一个不可变的终值

拒绝态(Rejected)

处于拒绝态时,promise 需满足以下条件:

  • 不能迁移至其他任何状态
  • 必须拥有一个不可变的据因

这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变(指当 value 或 error 不是基本值时,只要求其引用地址相等,但属性值可被更改)

Then 方法

一个 promise 必须提供一个 then 方法以访问其当前值终值拒因

promise 的 then 方法接受两个参数:

1
promise.then(onFulfilled, onRejected);

参数可选

onFulfilledonRejected 都是可选参数。

  • 如果 onFulfilled 不是函数,其必须被忽略
  • 如果 onRejected 不是函数,其必须被忽略

onFulfilled 特性

如果 onFulfilled 是函数:

  • promise 执行结束后其必须被调用,其第一个参数为 promise 的终值
  • promise 执行结束前其不可被调用
  • 其调用次数不可超过一次

onRejected 特性

如果 onRejected 是函数:

  • promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因
  • promise 被拒绝执行前其不可被调用
  • 其调用次数不可超过一次

调用时机

onFulfilledonRejected 只有在执行环境堆栈仅包含平台代码时才可被调用

调用要求

onFulfilledonRejected 必须被作为函数调用(即没有 this 值)

多次调用

then 方法可以被同一个 promise 调用多次

  • promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
  • promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

返回

then 方法必须返回一个 promise 对象

1
promise2 = promise1.then(onFulfilled, onRejected);
  • 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程[[Resolve]](promise2, x)
  • 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
  • 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
  • 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因

理解上面的“返回”部分非常重要,即:不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected

Promise 解决过程

Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 xthen 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise

这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

运行 [[Resolve]](promise, x) 需遵循以下步骤:

xpromise 相等

如果 promisex 指向同一对象,以 TypeError 为据因拒绝执行 promise

x 为 Promise

如果 x 为 Promise ,则使 promise 接受 x 的状态 :

  • 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
  • 如果 x 处于执行态,用相同的值执行 promise
  • 如果 x 处于拒绝态,用相同的据因拒绝 promise

x 为对象或函数

如果 x 为对象或者函数:

  • x.then 赋值给 then
  • 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
  • 如果then是函数,将x作为函数的作用域this调用之。传递两个回调函数作为参数,第一个参数叫做resolvePromise,第二个参数叫做rejectPromise:
    • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
    • 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
    • 如果 resolvePromiserejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略下的调用
  • 如果调用then方法抛出了异常e
    • 如果 resolvePromiserejectPromise 已经被调用,则忽略之
    • 否则以 e 为据因拒绝 promise
  • 如果 then 不是函数,以 x 为参数执行 promise
  • 如果 x 不为对象或者函数,以 x 为参数执行 promise

如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise

Promise 有什么用(特点/优缺点)

Promise 用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
new Promise((resolve,reject)=>{
//resolve([value]) or reject([error])
}).then(
res=>{

},
err=>{

}
).then(
res=>{

}
,err=>{

}
).catch(
err => console.log(err)
)
Promise.resolve();
Promise.reject();
Promise.all([promise1,promise2,...]).then();
Promise.race([promise1,promise2,...]).then();
  • Promise的构造方法接收一个executor(),在new Promise()时就立刻执行这个 executor 回调
  • executor()内部的异步任务被放入宏/微任务队列,等待执行
  • then()被执行,收集成功/失败回调,放入成功/失败队列
  • executor()的异步任务被执行,触发resolve/reject,从成功/失败队列中取出回调依次执行
  • 状态改变后触发 then、catch 的回调
  • Promise 拥有静态方法 resolve、reject、all、race

仔细一看,这不就是观察者模式:then 收集依赖 -> 异步触发 resolve -> resolve 执行依赖

根据以上写出如下框架代码

1
2
3
4
5
6
7
8
9
10
11
class Promise {
// 构造方法接收一个回调
constructor(exector) {}
then() {}
catch() {}
static resolve() {}
static reject() {}
static race() {}

static all() {}
}

首先定义三种状态

然后在构造函数中执行 exector()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const PENDING = "PENDING"; // 进行中
const FULFILLED = "FULFILLED"; // 已成功
const REJECTED = "REJECTED"; // 已失败

class Promise {
constructor(exector) {
this.status = PENDING; //初始化状态
this.value = undefined; //准备值和错误结果
this.error = undefined;

const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
const reject = (error) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = error;
}
};
exector(resolve, reject);
// 立即执行exector
// 把内部的resolve和reject方法传入executor,这样使用者可以调用resolve和reject方法
}
}

接下来实现 then 方法,then 方法可以访问其当前值终值拒因,接受两个参数onFulfilledonRejected,依据 Promise A+规范实现相关特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const status = {
pending: "PENDING",
fulfiled: "FULFILED",
rejected: "REJECTED",
};
class Promise {
constructor(exector) {
this.status = PENDING; //初始化状态
this.value = undefined; //准备值和错误结果
this.error = undefined;

const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
};
const reject = (error) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.value = error;
}
};
try {
exector(resolve, reject);
} catch (e) {
reject(e);
}
// 立即执行exector
// 把内部的resolve和reject方法传入executor,这样使用者可以调用resolve和reject方法
}
//then可以捕获exector的错误和结果,访问当前值,终值和拒因 所以需要try catch exector执行的错误
then(onFulfilled, onRejected) {
// then是微任务,这里用setTimeout模拟
setTimeout(() => {
if (this.status === FULFILLED) {
onFulfilled(this.value);
} else if (this.status === REJECTED) {
onRejected(this.error);
}
});
}
}

测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const promise = new Promise((resolve, reject) => {
console.log("1");
setTimeout(() => {
console.log("4");
});
Math.random() < 0.5 ? resolve(3) : reject(-3);
console.log("1.5");
}).then(
(res) => console.log(res),
(err) => console.log(err)
);

console.log("2");

//有结果 1 1.5 2 4 3/-3 异步调用问题
console.log(promise); //undefined 还需解决链式调用问题

then参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,当传入 then 的不是函数的时候,这个 then 是无效的。

原理上是当 then 中传入的不算函数,则这个promise返回上一个promise的值,这就是发生值穿透的原因,所以只需要对then的两个参数进行设置就行了:

1
2
3
4
5
6
7
8
9
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;

onRejected =
typeof onRejected === "function"
? onRejected
: (error) => {
throw new Error(error instanceof Error ? error.message : error);
};

开发中经常会将接口放于promise内部,等接口请求响应成功把数据resolve出去,或失败时把数据reject出去,此时thencatch才会进行捕获。

而现在的代码,promise内部如果有异步代码执行后才resolvethen不会等待异步代码执行完毕会直接执行,所以此时状态是PENDING,不会触发then的回调函数。

constructor中新增_resolveQueue_rejectQueue维护成功态、失败态任务队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 成功态回调函数队列
this._resolveQueue = [];
// 失败态回调函数队列
this._rejectQueue = [];
const resolve = (value) => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 新增代码:
// 成功态函数依次执行
this._resolveQueue.length > 0 && this._resolveQueue.forEach((fn) => fn(this.value));
//while(this._resolveQueue.length > 0){
// const callback = this._resolveQueue.shift();
// callback(this.value)
//}
}
};
const reject = (error) => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = REJECTED;
this.error = error;
// 新增代码:
// 失败态函数依次执行
this._rejectQueue.length > 0 this._rejectQueue.forEach((fn) => fn(this.error));
//while(this._rejectQueue.length > 0){
// const callback = this._rejectQueue.shift();
// callback(this.value)
//}
}
};

then(onFulfilled, onRejected) {
// then是微任务,这里用setTimeout模拟
setTimeout(() => {
// 新增代码:
if (this.status === PENDING) {
// 状态是PENDING下执行
// 说明promise内部有异步代码执行,还未改变状态,添加到成功/失败回调任务队列即可
this._resolveQueue.push(onFulfilled);
this._rejectQueue.push(onRejected);
}else if (this.status === FULFILLED) {
// FULFILLED状态下才执行
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// REJECTED状态下才执行
onRejected(this.error);
}
})

Promise的一大优势就是支持链式调用,具体来说就是then方法的具体实现,实际上是返回了一个Promise,需要注意的几个点:

  1. 保存之前 promise 实例的引用,即保存this
  2. 根据then回调函数执行的返回值
  • 如果是 promise 实例,那么返回的下一个 promise 实例会等待这个 promise 状态发生变化
  • 如果不是 promise 实例,根据目前情况直接执行resolvereject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function'? onRejected:
error => { throw new Error(error instanceof Error ? error.message:error) }
// 保存this
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING) {
//注意这个this指向目前的Promise对象,而不是新的Promise
//再强调一遍,很重要:
//目前的Promise(不是这里return的新Promise)的resolve和reject函数其实一个作为微任务
//因此他们不是立即执行,而是等then调用完成后执行
(this.) self._resolveQueue.push(() => {
// try捕获错误
try {
// 模拟微任务
setTimeout(() => {
const result = onFulfilled(self.value);
// 分两种情况:
// 1. 回调函数返回值是Promise,执行then操作
// 2. 如果不是Promise,调用新Promise的resolve函数

//下面执行之后的下一步,也就是记录执行的状态,决定新Promise如何表现
//如果返回值x是一个Promise对象,就执行then操作
//如果不是Promise,直接调用新Promise的resolve函数,
//新Promise的fulfilAry现在为空,在新Promise的then操作后.新Promise的resolve执行
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
});
self._rejectQueue.push(() => {
// 以下同理
try {
setTimeout(() => {
const result = onRejected(self.error);
// 不同点:此时是reject
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
})
} else if (self.status === FULFILLED) {
try {
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
});
} catch(e) {
reject(e);
}
} else if (self.status === REJECTED){
try {
setTimeout(() => {
const result = onRejected(self.error);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
}
});
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() < 0.5 ? resolve(100) : reject(-100);
}, 1000);
});

let p2 = p1.then((result) => {
//执行then返回的是一个新的Promise
return result + 100;
});
let p3 = p2.then(
(result) => {
console.log(result);
},
(reason) => {
console.log(reason);
}
);

catch()方法

Promise.prototype.catch就是Promise.prototype.then(null, onRejected)的别名,所以实现就很简单了:

1
2
3
catch(onRejected) {
return this.then(null, onRejected);
}

resolve()

这里就不考虑参数是thenable对象了,那么参数有两种情况:

  1. Promise实例
  2. 不是Promise实例
1
2
3
4
5
6
7
8
9
10
static resolve(value) {
if (value instanceof Promise) {
// 如果是Promise实例,直接返回
return value;
} else {
// 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}

reject()

Promise.reject也会返回一个 Promise 实例,状态为REJECTED

Promise.resolve不同的是,Promise.reject方法的参数会原封不动地作为reject的参数

1
2
3
4
5
6
static reject(error) {
return new Promise((resolve, reject) => {
reject(error);
})
}

all()

返回一个 promise 对象,只有当所有 promise 都成功时返回的 promise 状态才成功,需要注意的点是:

  1. 所有的 promise 状态变为FULFILLED,返回的 promise 状态才变为FULFILLED
  2. 一个 promise 状态变为REJECTED,返回的 promise 状态就变为REJECTED
  3. 数组成员不一定都是 promise,需要使用Promise.resolve()处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static all(promiseArr) {
let result = []
// 记录已经成功执行的promise个数
let count = 0;
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
// Promise.resolve()处理,确保每一个都是promise实例
Promise.resolve(p).then(
val => {
values[i] = val;
count++;
// 如果全部执行完,返回promise的状态就可以改变了
if (count === promiseArr.length) resolve(values);
},
err => reject(err), //只要有一个Promise被reject时,MyPromise的状态变为reject
);
}
})
}

race()

1
2
3
4
5
6
7
8
9
10
11
static race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(
val => resolve(val),
err => reject(err),
)
})
})
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// 模拟实现Promise
// Promise利用三大手段解决回调地狱:
// 1. 回调函数延迟绑定
// 2. 返回值穿透
// 3. 错误冒泡

// 定义三种状态
const PENDING = "PENDING"; // 进行中
const FULFILLED = "FULFILLED"; // 已成功
const REJECTED = "REJECTED"; // 已失败

class Promise {
constructor(exector) {
if (typeof executor !== "function") {
// 非标准 但与Chrome谷歌保持一致
throw TypeError("Promise resolver " + executor + " is not a function");
}

// 初始化状态
this.status = PENDING;
// 将成功、失败结果放在this上,便于then、catch访问
this.value = undefined;
this.error = undefined;
// 成功态回调函数队列
this._resolveQueue = [];
// 失败态回调函数队列
this._rejectQueue = [];

const resolve = (value) => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 成功态函数依次执行
this._resolveQueue.length > 0 &&this._resolveQueue.forEach((fn) => fn(this.value));
}
};
const reject = (error) => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = REJECTED;
this.error = error;
// 失败态函数依次执行
this._rejectQueue.length > 0 && this._rejectQueue.forEach((fn) => fn(this.error));
}
};
try {
// 立即执行executor
// 把内部的resolve和reject传入executor,用户可调用resolve和reject
exector(resolve, reject);
} catch (e) {
// executor执行出错,将错误内容reject抛出去
reject(e);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected = typeof onRejected === "function"
? onRejected
: (error) => {
throw new Error(error instanceof Error ? error.message : error);
};
// 保存this
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING) {
self._resolveQueue.push(() => {
// try捕获错误
try {
// 模拟微任务
setTimeout(() => {
const result = onFulfilled(self.value);
// 分两种情况:
// 1. 回调函数返回值是Promise,执行then操作
// 2. 如果不是Promise,调用新Promise的resolve函数
result instanceof Promise
? result.then(resolve, reject)
: resolve(result);
});
} catch (e) {
reject(e);
}
});
self._rejectQueue.push(() => {
// 以下同理
try {
setTimeout(() => {
const result = onRejected(self.error);
// 不同点:此时是reject
result instanceof Promise
? result.then(resolve, reject)
: resolve(result);
});
} catch (e) {
reject(e);
}
});
} else if (self.status === FULFILLED) {
try {
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise
? result.then(resolve, reject)
: resolve(result);
});
} catch (e) {
reject(e);
}
} else if (self.status === REJECTED) {
try {
setTimeout(() => {
const result = onRejected(self.error);
result instanceof Promise
? result.then(resolve, reject)
: resolve(result);
});
} catch (e) {
reject(e);
}
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
//finally方法
finally(callback) {
return this.then(
value => Promise.resolve(callback()).then(() => value), //执行回调,并returnvalue传递给后面的then
reason => Promise.resolve(callback()).then(() => { throw reason }) //reject同理
)
}
static resolve(value) {
if (value instanceof Promise) {
// 如果是Promise实例,直接返回
return value;
} else {
// 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}
static reject(error) {
return new Promise((resolve, reject) => {
reject(error);
});
}
static all(promiseArr) {
let result = []
// 记录已经成功执行的promise个数
let count = 0;
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
// Promise.resolve()处理,确保每一个都是promise实例
Promise.resolve(p).then(
val => {
values[i] = val;
count++;
// 如果全部执行完,返回promise的状态就可以改变了
if (count === promiseArr.length) resolve(values);
},
err => reject(err), //只要有一个Promise被reject时,MyPromise的状态变为reject
);
}
})
}

static race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach((p) => {
Promise.resolve(p).then(
(val) => resolve(val),
(err) => reject(err)
);
});
});
}
}

9k 字 | Promise/async/Generator 实现原理解析