深入Promise

深入Promise

Promise/A+规范

Promise 规范有很多,如 Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+,有兴趣的可以去了解下,最终 ES6 中采用了 Promise/A+ 规范。在讲解 Promise 实现之前,当然要先了解 Promise/A+ 规范。Promise/A+ 规范参考:

其中有几个要点需要注意一下:

  1. Promise 本质是一个状态机。每个 promise 只能是 3 种状态中的一种:pending、fulfilled 或 rejected。状态转变只能是 pending -> fulfilled 或者 pending -> rejected。状态转变不可逆
  2. then 方法可以被同一个 promise 调用多次。
  3. then 方法必须返回一个 promise。规范里没有明确说明返回一个新的 promise 还是复用老的 promise(即 return this),大多数实现都是返回一个新的 promise,而且复用老的 promise 可能改变内部状态,这与规范也是相违背的。
实现一个简单的Promise

Promise 初始化

Promise对象存在3中状态:

  • Pending(进行中)
  • Fulfilled(已成功)
  • Rejected(已失败)

状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态。

resolvereject

  • resolve: 将promise对象的状态从Pending(进行中)变为Fulfilled(已完成)
  • reject: 将Promise对象的状态从Pending(进行中)变为Rejected(已失败)
  • resolve reject 都可以传入任意类型的值作为实参,表示 Promise 对象成功(Fulfilled)和失败(Rejected)的值
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
/**
* 实现一个Promise
*/

const PENDING = Symbol();
const FULFILLED = Symbol();
const REJECTED = Symbol();

class MyPromise {
constructor (fn) {
if (typeof fn !== 'function') {
throw new Error('fn must be a function!');
}
this._status = PENDING; // 状态
this._value = null;

// 执行fn
try {
fn(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err);
}
}

_resolve (val) {
if (this._status !== PENDING) return;
this._status = FULFILLED;
this._value = val;
}

_reject (err) {
if (this._status !== PENDING) return;
this._status = REJECTED;
this._value = err;
}
}

这样我们就是实现了Promise状态和值的改变。接下来我们要实现Promise和核心:then方法

完善Promise

实现Promise的then方法,Promise对象的then方法接受两个参数:

1
promise.then(onFulfilled, onRejected)

then方法中的两个参数如果不是函数,则必须被忽略

  • 如果onFulfilled是函数,则当Promise状态变为成功时,必须被调用,其中第一个参数为Promise成功状态传入的值(即resolve的值),只可以被调用一次。
  • 如果onRejected是函数,则当Promise状态变为失败时,必须被调用,其中第一个参数为Promise失败状态传入的值(即reject的值),只可以被调用一次
  • then方法可以被同一个Promise对象调用多次,当Promise状态变为成功时,所有onFulfilled按照注册顺序依次回调;当Promise状态变为失败时,所有onRejected按照注册顺序依次回调
  • then 方法必须返回一个Promise(在ES6 以及大多数实现中都是返回一个新的Promise),因此Promise的then方法支持链式调用

特殊的需要注意Promise的值传递以及错误传递的机制。

修改构造函数:增加执行队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
constructor (fn) {
if (typeof fn !== 'function') {
throw new Error('fn must be a function!');
}
this._status = PENDING; // 状态
this._value = null;
this._fulfilledQueues = []; // 添加成功回调函数队列
this._rejectedQueues = []; // 添加失败回调函数执行队列
// 执行fn
try {
fn(this._resolve.bind(this), this._reject.bind(this));
} catch (err) {
this._reject(err);
}
}

添加then方法,并且需要将回调函数加入到执行队列中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
then (onFulfilled, onRejected) {
switch (this._status) {
case PENDING:
this._fulfilledQueues.push(onFulfilled);
this._rejectedQueues.push(onRejected);
break;
case FULFILLED:
onFulfilled(this._value);
break;
case REJECTED:
onRejected(this._value);
break;
}
return new MyPromise((onFulfilledNext, onRejectedNext) => {

});
}

完善then方法:

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
then (onFulfilled, onRejected) {
return new MyPromise((onFulfilledNext, onRejectedNext) => {
let fulfilled = (value) => {
try {
if (!(typeof onFulfilled === 'function')) {
onFulfilledNext(value);
} else {
let result = onFulfilled(value);
if (result instanceof MyPromise) {
result.then(onFulfilledNext, onRejectedNext)
} else {
onFulfilledNext(result);
}
}
} catch (err) {
onRejectedNext(err);
}
};
let rejected = (error) => {
try {
if (!(typeof onRejected === 'function')) {
onRejectedNext(error);
} else {
let result = onRejected(error);
if (result instanceof MyPromise) {
result.then(onFulfilledNext, onRejectedNext);
} else {
onFulfilledNext(result);
}
}
} catch (err) {
onRejectedNext(err);
}
};
switch (this._status) {
case PENDING:
this._fulfilledQueues.push(fulfilled);
this._rejectedQueues.push(rejected);
break;
case FULFILLED:
fulfilled(this._value);
break;
case REJECTED:
rejected(this._value);
break;
}
});
}

then方法算是Promise的核心,then方法除了必须返回一个新的Promise之外(以便链式调用);需要注意then中注册的两个回调函数也可能是一个Promise~;以上then函数的代码需要参照Promise的定义认真琢磨!

接着修改 _resolve 和 _reject :依次执行队列中的函数

当 resolve 或 reject方法执行时,我们依次提取成功或失败任务队列当中的函数开始执行,并清空队列,从而实现 then 方法的多次调用,实现的代码如下:

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
_resolve (val) {
if (this._status !== PENDING) return;
// 依次执行队列中的回调函数
const run = () => {
this._status = FULFILLED;
this._value = val;
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(val);
}
};
// 为了支持同步的Promise,
setTimeout(run, 0);
}

_reject (err) {
if (this._status !== PENDING) return;
this._status = REJECTED;
this._value = err;
const run = () => {
this._status = REJECTED;
this._value = err;
let cb;
while (cb = this._rejectedQueues.shift()){
cb(err);
}
};
setTimeout(run, 0);
}

这里还有一种特殊的情况,就是当 resolve方法传入的参数为一个 Promise对象时,则该 Promise对象状态决定当前Promise对象的状态。我们需要修改_resolve来支持这样的特性:

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
_resolve (val) {
// 依次执行队列中的回调函数
const run = () => {
if (this._status !== PENDING) return;
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value);
}
};
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error);
}
};

if (val instanceof MyPromise) {
val.then(value => {
this._value = value;
this._status = FULFILLED;
runFulfilled(value);
}, error => {
this._value = error;
this._status = REJECTED;
runRejected(error);
});
} else {
this._value = val;
this._status = FULFILLED;
runFulfilled(val);
}
};
// 为了支持同步的Promise,
setTimeout(run, 0);
}

这样一个基本的Promise就实现了。我们可以为他添加一些其他的方法

catch方法

相当于调用then方法,只需要注册Rejected状态的回调函数

1
2
3
4
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected);
}

静态resolve方法

1
2
3
4
5
6
// 添加静态resolve方法
static resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}

静态rejected方法

1
2
3
4
// 添加静态reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}

静态all方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 添加静态all 方法
static all (list) {
return new MyPromise((resolve, reject) => {
let values = [];
let count = 0;
for (let i in list) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(list[i]).then(res => {
values[i] = res;
count++;
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values);
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err);
});
}
});
}

静态race方法

1
2
3
4
5
6
7
8
9
10
11
12
static race (list) {
return new MyPromise((resolve, reject) => {
for (let v of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(v).then(res => {
resolve(res);
}, err => {
reject(err);
});
}
});
}

finally方法

finally 方法用于指定不管 Promise对象最后状态如何,都会执行的操作

1
2
3
4
5
6
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
};

这样一个完整的Promise就实现了~

完整代码如下:

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
178
179
/**
* 实现一个Promise
*/

const PENDING = Symbol();
const FULFILLED = Symbol();
const REJECTED = Symbol();

class MyPromise {
constructor (fn) {
if (typeof fn !== 'function') {
throw new Error('fn must be a function!');
}
this._status = PENDING; // 状态
this._value = null;
this._fulfilledQueues = []; // 添加成功回调函数队列
this._rejectedQueues = []; // 添加失败回调函数执行队列
// 执行fn
try {
fn(this._resolve.bind(this), this._reject.bind(this));
} catch (err) {
this._reject(err);
}
}

_resolve (val) {
// 依次执行队列中的回调函数
const run = () => {
if (this._status !== PENDING) return;
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value);
}
};
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error);
}
};

if (val instanceof MyPromise) {
val.then(value => {
this._value = value;
this._status = FULFILLED;
runFulfilled(value);
}, error => {
this._value = error;
this._status = REJECTED;
runRejected(error);
});
} else {
this._value = val;
this._status = FULFILLED;
runFulfilled(val);
}
};
// 为了支持同步的Promise,
setTimeout(run, 0);
}

_reject (err) {
if (this._status !== PENDING) return;
this._status = REJECTED;
this._value = err;
const run = () => {
this._status = REJECTED;
this._value = err;
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err);
}
};
setTimeout(run, 0);
}

then (onFulfilled, onRejected) {
return new MyPromise((onFulfilledNext, onRejectedNext) => {
let fulfilled = (value) => {
try {
if (!(typeof onFulfilled === 'function')) {
onFulfilledNext(value);
} else {
let result = onFulfilled(value);
if (result instanceof MyPromise) {
result.then(onFulfilledNext, onRejectedNext);
} else {
onFulfilledNext(result);
}
}
} catch (err) {
onRejectedNext(err);
}
};
let rejected = (error) => {
try {
if (!(typeof onRejected === 'function')) {
onRejectedNext(error);
} else {
let result = onRejected(error);
if (result instanceof MyPromise) {
result.then(onFulfilledNext, onRejectedNext);
} else {
onFulfilledNext(result);
}
}
} catch (err) {
onRejectedNext(err);
}
};
switch (this._status) {
case PENDING:
this._fulfilledQueues.push(fulfilled);
this._rejectedQueues.push(rejected);
break;
case FULFILLED:
fulfilled(this._value);
break;
case REJECTED:
rejected(this._value);
break;
}
});
}

// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected);
}

static resolve (value) {
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
}

static reject (error) {
return new MyPromise((resolve, reject) => reject(error));
}

static all (list) {
return new MyPromise((resolve, reject) => {
let values = [];
let count = 0;
for (let i in list) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(list[i]).then(res => {
values[i] = res;
count++;
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values);
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err);
});
}
});
}

static race (list) {
return new MyPromise((resolve, reject) => {
for (let v of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(v).then(res => {
resolve(res);
}, err => {
reject(err);
});
}
});
}

static finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason;})
);
}
}

参考:

Promise实现原理