手撕Promise
观看之前请先熟悉promise的基本使用
# 一、剖析promise由哪些东西组成
在日常的使用中,我们可以知道,一个完整的promise应当包含成功、失败、进行中这三个状态,同时还有resolve,reject两个改变状态的成员函数。最后还会有一个then函数来接收promise的执行结果
在了解了上述的内容之后,一个promise的雏形就出来了
class myPromise {
constructor(executor) {
//默认状态为pending
this.status = 'pending'
//成功的默认值为undefined
this.successVal = undefined
//失败的默认值为undefined
this.errorVal = undefined
//初始化时调用传入的回调函数
executor(this.resolve,this.reject)
}
resolve = (data) => {
}
reject = (err) => {
}
then = (success_cb,error_cb) => {
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
这里要说明一下,then函数,可能在日常使用中大家都习惯性的只传入一个参数。其实then是有两个参数的,第一个参数是promise成功时调用,第二个参数是失败时调用。而我们熟知的catch函数,其实内部就是调用的then函数,只不过传入的是失败的回调
catch = (err_cb) => {
this.then(undefined, err_cb)
}
2
3
下面来完善一下几个函数的内容
resolve = (data) => {
if (this.status === 'pending') {
this.status = 'success'
this.successVal = data
}
}
reject = (err) => {
if (this.status === 'pending') {
this.status = 'error'
this.errorVal = err
}
}
then = (success_cb,error_cb) => {
setTimeout(() => {
if (this.status === 'success') {
success_cb(this.successVal )
}else if(this.status === 'error'){
success_cb(this.errorVal )
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
这样,一个简易版的promise就实现了。
接下来就需要去实现promise处理异步问题。
首先分析一下,出现了异步代码之后会发生什么情况: 通常使用一个有异步的promise会这样
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(123)
},2000)
})
2
3
4
5
}) 这样我们在调用then的时候其实resolve还没有被执行,promise的状态也并没有变更。所以我们要做的就是讲then函数接收到的参数存起来,等到promise的状态变了之后再去调用他。这样就能做到处理异步问题了
constructor(executor) {
//默认状态为pending
this.status = 'pending'
//成功的默认值为undefined
this.successVal = undefined
//失败的默认值为undefined
this.errorVal = undefined
//存放成功的回调函数
this.successCallBackList = []
//存放失败的回调函数
this.errorCallBackList = []
//自动执行一次传入的回调函数
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
resolve = (data) => {
if (this.status === 'pending') {
this.status = 'success'
this.successVal = data
this.successCallBackList.forEach(fn => fn(this.successVal))
}
}
reject = (err) => {
if (this.status === 'pending') {
this.status = 'error'
this.errorVal = err
this.errorCallBackList.forEach(fn => fn(this.errorVal))
}
}
then = (success_cb,error_cb) => {
setTimeout(() => {
if (this.status === 'success') {
success_cb(this.successVal )
}else if(this.status === 'error'){
success_cb(this.errorVal )
}else{
this.successCallBackList.push(success_cb)
this.errorCallBackList.push(error_cb)
}
})
}
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
上面代码中就是在then里判断当前状态,如果为pending的话就将回调存入对应的数组,然后再resolve和reject函数中去遍历存放回调的数组并且一一执行里头的函数。
这样异步问题也处理好了。 接下来就是最重要的处理then的链式回调了。这也是promise的特色。
首先需要明白的一点是,promise的then函数会返回一个新的promise,这样我们才可以做到一直.then调用。其次,后面的then会拿到上一个then的返回值。 明白了这个概念之后我们就可以去着手写then函数了
then = (success_cb, error_cb) => {
const promise2 = new myPromise((resolve, reject) => {
if (this.status === 'success') {
setTimeout(() => {
try {
if (typeof success_cb !== 'function') {
resolve(this.successVal)
} else {
let x = success_cb(this.successVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
} else if (this.status === 'error') {
setTimeout(() => {
try {
if (typeof error_cb !== 'function') {
reject(this.errorVal)
} else {
let x = error_cb(this.errorVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
} else {
this.successCallBackList.push(() => {
setTimeout(() => {
try {
if (typeof success_cb !== 'function') {
resolve(this.successVal)
} else {
let x = success_cb(this.successVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
})
this.errorCallBackList.push(() => {
setTimeout(() => {
try {
if (typeof error_cb !== 'function') {
reject(this.errorVal)
} else {
let x = error_cb(this.errorVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
})
}
}
)
return promise2
}
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
/**
* @param promise2 promise1.then中返回的新的promise
* @param x promise1中then方法的回调函数返回的值
* @param resolve promise2的reslove回调
* @param reject promise2的reject回调
*/
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
/**
* 如果x等于promis2 则抛出异常,防止陷入死循环 抛出的异常会被上一个promise的then方法捕获到并且直接走reject方法
*/
throw new TypeError('Chaining cycle detected for promise');
}
if (x instanceof myPromise) {
/**
* 如果 x 为 Promise ,则使 promise2 接受 x 的状态
* 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
*/
x.then(y => {
resolvePromise(promise2, y, resolve, reject)
}, reject)
} else if (x !== null && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
var then = x.then
} catch (e) {
return reject(e)
}
if (typeof then === 'function') {
let called = false //防止多次调用
try {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x)
}
} else {
resolve(x)
}
}
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
resolvePromise函数就是处理上一个then函数的返回值的工具函数。
then函数里的逻辑大致上就是,返回一个新的promise,这个新的promise里可以访问到上一个promise的所有属性,同时可以通过调用自身的resolve和reject改变自身的状态,并将上一个promise的then函数的返回值作为参数保存下来,这样他自己的then函数就可以访问到这个值了。也就是我们所说的下一个then能拿到上一个then的返回值。而在then函数中可能返回各种情况的值,所以写了一个resolvePromise函数来处理各种情况。
以上就是一个完整的promise了。重要的就是理解then函数的链式回调。把这个搞明白了,整个promise也就豁然开朗了。
另外附上完整的代码
/**
* @param promise2 promise1.then中返回的新的promise
* @param x promise1中then方法的回调函数返回的值
* @param resolve promise2的reslove回调
* @param reject promise2的reject回调
*/
function resolvePromise(promise2, x, resolve, reject) {
if (x === promise2) {
/**
* 如果x等于promis2 则抛出异常,防止陷入死循环 抛出的异常会被上一个promise的then方法捕获到并且直接走reject方法
*/
throw new TypeError('Chaining cycle detected for promise');
}
if (x instanceof myPromise) {
/**
* 如果 x 为 Promise ,则使 promise2 接受 x 的状态
* 也就是继续执行x,如果执行的时候拿到一个y,还要继续解析y
*/
x.then(y => {
resolvePromise(promise2, y, resolve, reject)
}, reject)
} else if (x !== null && ((typeof x === 'object') || (typeof x === 'function'))) {
try {
var then = x.then
} catch (e) {
return reject(e)
}
if (typeof then === 'function') {
let called = false //防止多次调用
try {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x)
}
} else {
resolve(x)
}
}
class myPromise {
//静态resolve方法,可以让使用者直接通过类来访问,不需要实例化这个类。同时以static关键字标识的属性不会被实例继承,只能通过类自身访问
static resolve() {
}
constructor(executor) {
//默认状态为pending
this.status = 'pending'
//成功的默认值为undefined
this.successVal = undefined
//失败的默认值为undefined
this.errorVal = undefined
//存放成功的回调函数
this.successCallBackList = []
//存放失败的回调函数
this.errorCallBackList = []
//自动执行一次传入的回调函数
try {
executor(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
resolve = (data) => {
if (this.status === 'pending') {
this.status = 'success'
this.successVal = data
this.successCallBackList.forEach(fn => fn(this.successVal))
}
}
reject = (err) => {
if (this.status === 'pending') {
this.status = 'error'
this.errorVal = err
this.errorCallBackList.forEach(fn => fn(this.errorVal))
}
}
then = (success_cb, error_cb) => {
const promise2 = new myPromise((resolve, reject) => {
if (this.status === 'success') {
setTimeout(() => {
try {
if (typeof success_cb !== 'function') {
resolve(this.successVal)
} else {
let x = success_cb(this.successVal)
//简易处理then
if(x instanceof myPromise){
if(x.status==='pending'){
x.then()
}else{
//x的状态不处于pending状态,则将新的promise的resolve和reject作为x的then函数的参数传入
//这样,当x的状态变更了,就会调用我们传入的resolve和reject。并且会将x的最终值传入。
x.then(resolve, reject)
}
}else{
resolve(x)
}
// resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
} else if (this.status === 'error') {
setTimeout(() => {
try {
if (typeof error_cb !== 'function') {
reject(this.errorVal)
} else {
let x = error_cb(this.errorVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
} else {
this.successCallBackList.push(() => {
setTimeout(() => {
try {
if (typeof success_cb !== 'function') {
resolve(this.successVal)
} else {
let x = success_cb(this.successVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
})
this.errorCallBackList.push(() => {
setTimeout(() => {
try {
if (typeof error_cb !== 'function') {
reject(this.errorVal)
} else {
let x = error_cb(this.errorVal)
resolvePromise(promise2, x, resolve, reject)
}
} catch (err) {
reject(err)
}
})
})
}
}
)
return promise2
}
catch = (err_cb) => {
this.then(undefined, err_cb)
}
}
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