来源:https://www.jianshu.com/p/43de678e918a

前言

面试中,面试官让我写一个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
class MyPromise {
constructor(func) {
this.state = 'pendding'
this.value = undefined;
const resolved = (value) => {
this.state = 'fulfilled';
this.value = value;
}
const rejected = (error) => {
this.state = 'rejected';
this.value = error;
}
try {
func(resolved, rejected);
} catch(e) {
rejected(e);
}
}

then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
}
if (this.state === 'fulfilled') {
onRejected(this.value);
}
}
}

然后面试官问,如果遇到中间状态怎么办?Promise是支持多个then方法调用的,你的这个支持么?

那一瞬间,我才感觉到,其实我对Promise的原理理解的很片面,很多问题都没有深入核心去理解,虽然面试通过了,但是也暴露了问题,所以有必要针对Promise的原理写一遍笔记来加深印象。

Promise的基本结构

1
2
3
new Promise(function(resolve, reject) {
resolve('fulfilled');
})

构造函数Promise必须要接受一个函数作为参数,该函数成为handlehandle又包含resolvereject两个参数,他们是两个函数。

首先,定义一个MyPromseclass,接收一个handle作为参数:

1
2
3
4
5
6
7
class MyPromise {
constructor(handle) {
if (typeof handel !== 'funciton') {
throw new Error('MyPromise 必须接收一个函数作为参数!');
}
}
}

Promise的状态和值

Promise存在三种状态:

  • Pendding(进行中)

  • Fulfilled(已成功)

  • Rejected(已失败)

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

Promise的值是指状态改变时传递给回调函数的值:上面的handle函数包含的两个resolvereject两个函数参数,可以用于改变Promise的状态和传入Promise值:

  • resolve:将Promise对象的状态从Pendding状态改为Fulfilled

  • reject:将Promise对象的状态从Pendding状态改为Rejected

  • resolvereject都可以传入任意类型的值作为实参,表示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
class MyPromise {
constructor(handle) {
if (typeof handel !== 'funciton') {
throw new Error('MyPromise 必须接收一个函数作为参数!');
}
// 添加状态
this.state = 'Pendding';
// 添加值
this.value = undefined;
// 执行handle
try {
handle(this.resolve.bind(this), this.reject.bind(this));
} catch(err) {
this.reject(err);
}
}
// resolve时执行的函数
resolve(value) {
if (this.state !== 'Pendding') {
this.state = 'Fulfilled';
this.value = value;
}
}
// reject时执行的函数
reject(err) {
if (this.state !== 'Pendding') {
this.state = 'Rejected';
this.value = err;
}
}
}

Promisethen方法

上面的代码,其实和我面试中写的就差不多一样了,接下来就会解答面试官提出的问题了

then方法的参数:

Promise对象的then方法接收两个参数(两个参数都是可选参数,如果不是函数,则必须被忽略):

  • onFulfilled

    • Promise状态变为成功时必须被调用,其第一个参数为Promise对象成功状态传入的值(resolve执行时传入的值)

    • Promise状态改变前其不可被调用

    • 调用次数不可超过一次

  • onRejected

    • Promise状态变为失败时必须被调用,其第一个参数为Promise对象成功状态传入的值(reject执行时传入的值)

    • Promise状态改变前其不可被调用

    • 调用次数不可超过一次

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

  • Promise成功状态时,所有的onFulfilled需按照其注册顺序依次回调

  • Promise失败状态时,所有的onRejected需按照其注册顺序依次回调

then方法必须返回一个新的Promise对象, 因此Promise支持链式调用

  1. 如果onFulfilled或者onRejected返回一个值x,则运行下面的Promise解决过程:

    • x不为Promise:则x直接作为新返回的Promise对象的值,即新的onFulfilled或者onRejected函数的参数

    • xPromise:此时返回一个回调函数,会等待该Promise对象(即x)的状态发生变化后,才会被调用,并且新的Promise对象的状态和x的状态相同。

    下面是用于解释的例子:

    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
      const promise1 = new Promise(function(resolve, reject) {
    setTimeout(function() {
    resolve();
    }, 1000);
    });

    const promise2 = promise1.then(function(res) {
    return '这里返回的是一个普通值';
    })

    promise2.then(function(res) {
    console.log(res); // 1秒后打印出:这里返回的是一个普通值
    });
    ```

    ```javascript
    const promise1 = new Promise(function(resolve, reject) {
    setTimeout(function() {
    resolve();
    }, 1000);
    });

    const promise2 = promise1.then(function(res) {
    return new Promise(function(resolve, reject) {
    setTimeout(funciton() {
    resolve('这里返回的是一个Pormise');
    }, 2000);
    });
    })

    promise2.then(function(res) {
    console.log(res); // 3秒后打印出:这里返回的是一个Pormise
    });
    ```

    2. 如果`onFulfilled`或者`onRejected`抛出一个异常`e`,则`promise2`必须变为失败,并且返回一个失败的值`e`,如下:

    ```javascript
    const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve('success')
    }, 1000)
    })
    const promise2 = promise1.then(res => {
    throw new Error('这里抛出一个异常e')
    })
    promise2.then(res => {
    console.log(res)
    }, err => {
    console.log(err) //1秒后打印出:这里抛出一个异常e
    })
  2. 如果onFulfilled不是函数且promise1状态为成功,promise2必须变为成功并返回promise1的值,例如:

1
2
3
4
5
6
7
8
9
10
11
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
const promise2 = promise1.then('这里的onFulfilled本来是一个函数,但是现在是值');
promise2.then(res => {
console.log(res) //1秒后打印出:success
}, err => {
console.log(err)
})
  1. 如果onRejected不是函数且promise1状态为失败,promise2必须变为失败并返回promise1失败的值,例如:
1
2
3
4
5
6
7
8
9
10
11
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
})
const promise2 = promise1.then(res => res, '这里的onFulfilled本来是一个函数,但是现在是值');
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) //1秒后打印出:fail
})

根据以上规则,来给MyPromise增加一个执行队列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyPromise {
constructor(handle) {
if (typeof handel !== 'funciton') {
throw new Error('MyPromise 必须接收一个函数作为参数!');
}
// 添加状态
this.state = 'Pendding';
// 添加值
this.value = undefined;
// 添加成功回调函数的队列
this.fulfilledQueues = [];
// 添加失败回调函数的队列
this.rejectedQueues = [];
// 执行handle
try {
handle(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
18
then(onFulfilled, onRejected){
const { state, value } = this;
switch(state) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case 'Pendding':
this.fulfilledQueues.push(onFulfilled);
this.rejectedQueues.push(onRejected);
break;
case 'Fulfilled':
onFullfilled(value);
break;
case 'Rejected':
onRejected(value);
break;
}
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {});
}

那么新返回的Promise对象什么时候改变状态?改变为那种状态呢?

根据上文中then方法的规则,新返回的Promise对象状态依赖于当前then方法的回调函数执行的情况以及返回值,如:then的参数是否为一个函数,回调函数执行是否出错,返回值是否为Pormise对象

依照上面的规则,可以进一步完善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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
then(onFulfilled, onRejected) {
const { state, value } = this;
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封装一个成功时执行的函数
const fulfilled = value => {
try {
if (typeof onFulfilledNext !== 'funciton') {
// 如果onFulfilled不是一个函数,则value作为新的onFulfilled的值
onFulfilledNext(value);
} else {
const res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回的是一个Promise对象,必须等待其状态改变后在执行下一个函数
res.then(onFulfilledNext, onRejectedNext);
} else {
// 如果当前回调函数不是一个Promise对象,则返回结果直接作为一个参数,掺入下一个then的回调函数,并立即执行then的回调函数
onFulfilledNext(value);
}
}
} catch(err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err);
}
}
// 封装一个失败时执行的函数
const rejected = err => {
try {
if (typeof onRejected !== 'funciton') {
// 如果onRejected不是一个函数,则value作为新的onRejected的值
onRejectedNext(err);
} else {
const res = onRejected(err);
if (res instanceof MyPromise) {
// 如果返回对象是一个Promise对象,则必须等待其装填改变后在执行下一个函数
res.then(onFulfilledNext, onRejectedNext);
} else {
// 如果当前回调函数不是一个Promise对象,则返回结果直接作为一个参数,掺入下一个then的回调函数,并立即执行then的回调函数
onRejectedNext(err);
}
}
} catch(err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err);
}
}
switch(state) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case 'Pendding':
this.fulfilledQueues.push(fulfilled);
this.rejectedQueues.push(rejected);
break;
case 'Fulfilled':
fulfilled(value);
break;
case 'Rejected':
rejected(value);
break;
}
});

}

然后修改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
// resolve时执行的函数
resolve(value) {
if (this.state !== 'Pendding') {
return;
}
// 依次执行成功队列中的函数,并清空队列
const run = () => {
let tempFulfiiled;
this.state = 'Fulfilled';
this.value = value;
while(tempFulfiiled = this.fulfilledQueues.shift()) {
tempFulfiiled(value);
}
}
// 为了支持同步的Promise,这采用异步调用
setTimeout(() => run(), 0);
}
// reject时执行的函数
reject(err) {
if (this.state !== 'Pendding') {
return;
}
// 依次执行失败队列中的函数,并清空队列
const run = () => {
let tempRejected;
this.state = 'Rejected';
this.value = err;
while(tempRejected = this.rejectedQueues.shift()) {
tempRejected(err);
}
}
// 为了支持同步的Promise,这采用异步调用
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
37
38
39
resolve(value) {
const run = () => {
if (this.state !== 'Pendding') {
return;
}
this.state = 'Fulfilled';
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = value => {
let tempFulfilled;
while(tempFulfilled = this.fulfilledQueues.shitf()) {
tempFulfilled(value);
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = err => {
let tempRejected;
while(tempRejected = this.rejectedQueues.shift()) {
tempRejected(err);
}
}
/* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
*/
if (value instanceof MyPromise) {
val.then(value => {
this.value = value;
runFulfilled(value);
}, err => {
this.value = err;
runRejected(err);
});
} else {
this.value = value;
runFulfilled(value);
}
}
// 为了支持同步的Promise,这采用异步调用
setTimeout(() => run(), 0);
}

以上就是基本的Promise

Promise的其他方法

  • catch方法:相当于调用then方法,但是只有Rejected状态的回调函数
1
2
3
catch(onRejected) {
return this.then(undefined, onRejected);
}
  • 静态resolve方法
1
2
3
4
5
6
7
static resolve(value) {
// 如果是Promise对象,则直接返回该实例
if (value instanceof MyPormise) {
return value;
}
return new MyPromise(resolve => resolve(value));
}
  • 静态reject方法
1
2
3
static reject (error) {
return new Promise((resolve, reject) => reject(error));
}
  • 静态all方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static all(list) {
return MyPromise((resolve, reject) => {
// 返回值得集合
const values = [];
let count = 0;
for (let [i, p] in list.entries()) {
// 数组参数如果不是MyPromise实例,则先调用MyPormise.resolve
this.resolve(p).then(res => {
values[i] = res;
count++;
// 所有状态都变成Fulfilled时,返回的Promise状态就会变成Fulfilled
if (count === list.length) {
resolve(values);
}
}, err => {
// 如果有一个被rejected时,返回的Promise对象装填就变成Rejected
reject(err);
})
}
})
}
  • 静态race方法
1
2
3
4
5
6
7
8
9
10
11
12
static race(list) {
return MyPromise((resovle, reject) => {
// 只要有一个实例率先改变状态,则新的Promise的状态就跟着改变
for (let p of list) {
this.resolve(p).then(res => {
resovle(res);
}, err => {
reject(err);
});
}
});
}
  • finally方法: 不管Promise对象最后状态如何,都会执行的操作
1
2
3
4
5
6
7
finally(callback) {
return this.then(value => {
value => MyPromise.resolve(callback()).then(() => value);
}, err => {
err => MyPromise.reject(callback()).then(undefined, () => { throw 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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// 判断变量否为function
const isFunction = variable => typeof variable === 'function'
// 定义Promise的三种状态常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
constructor(handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加状态
this._status = PENDING
// 添加状态
this._value = undefined
// 添加成功回调函数队列
this._fulfilledQueues = []
// 添加失败回调函数队列
this._rejectedQueues = []
// 执行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle时执行的函数
_resolve(val) {
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次执行成功队列中的函数,并清空队列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次执行失败队列中的函数,并清空队列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
runFulfilled(value)
}, err => {
this._value = err
runRejected(err)
})
} else {
this._value = val
runFulfilled(val)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run, 0)
}
// 添加reject时执行的函数
_reject(err) {
if (this._status !== PENDING) return
// 依次执行失败队列中的函数,并清空队列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 为了支持同步的Promise,这里采用异步调用
setTimeout(run, 0)
}
// 添加then方法
then(onFulfilled, onRejected) {
const {
_value,
_status
} = this
// 返回一个新的Promise对象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封装一个成功时执行的函数
let fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err)
}
}
// 封装一个失败时执行的函数
let rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
res.then(onFulfilledNext, onRejectedNext)
} else {
//否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函数执行出错,新的Promise对象的状态为失败
onRejectedNext(err)
}
}
switch (_status) {
// 当状态为pending时,将then方法回调函数加入执行队列等待执行
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 当状态已经改变时,立即执行对应的回调函数
case FULFILLED:
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加静态resolve方法
static resolve(value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加静态reject方法
static reject(value) {
return new MyPromise((resolve, reject) => reject(value))
}
// 添加静态all方法
static all(list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一个被rejected时返回的MyPromise状态就变成rejected
reject(err)
})
}
})
}
// 添加静态race方法
static race(list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally(cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => {
throw reason
})
);
}
}