Promise:手写Promise,原理分析

源码地址:https://gitee.com/lagou-19-web-paid/practice/tree/master/promise

1- Promise 对象的基础模型 (同步,分析执行流程)

  1. Promise 是一个类,类接收的是一个构造函数,在执行类的时候这个构造函数也会同步执行。
  2. 构造函数在同步执行的时候,会传递俩个回调函数,分别是成功回调[resolve]、失败回调[reject]。
  • 成功回调:回调函数内接收一个参数,可以是一个普通值,也可以是一个 Promise 对象。此阶段暂时只考虑普通值。
  • 失败回调:回调函数内接收一个参数,一般为失败原因
  1. Promise 中有三个状态,分别为 等待[pending]、成功[fulfilled]、失败[rejected]。状态仅支持 pending -> fulfilled 或 pending -> rejected,状态一旦确定就不支持修改了。
  2. then 方法会接收俩个参数,成功回调[successCallback]函数、失败回调[failCallback]函数。在 then 方法执行时,会依据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
 
// Promise 的三个状态
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

/**
* 模拟 原生Promise
*
* @class MyPromise
* @constructor excutor 构造函数,函数接收俩个回调函数,回调函数
*/
class MyPromise {
constructor( excutor ) {
excutor(this.resolve, this.reject)
}

// 默认状态为 pending(等待)
status = PENDING

// 缓存状态值
value = undefined // 成功状态 【参数】
reason = undefined // 失败状态 【原因】


/**
* 成功回调
*
* @see 函数使用箭头函数,可以使 this 指向于当前 MyPromise 对象
*/
resolve = (value) => {
// 状态一旦确定不支持更改,return 出函数
if (this.status !== PENDING) return

// 更改状态为 fulfilled(成功)
this.status = FULFILLED

// 缓存成功回调 传递的值
this.value = value
}

/**
* 失败回调
*/
reject = (reason) => {
// 状态一旦确定不支持更改,return 出函数
if (this.status !== PENDING) return

// 更改状态为 rejected(失败)
this.status = REJECTED

// 缓存失败回调 传递的原因
this.reason = reason
}

/**
* then 方法
*
* @params {function} successCallback[成功回调]
* @params {function} failCallback[失败回调]
*/
then ( successCallback, failCallback ) {
if (this.status === FULFILLED) successCallback(this.value)
if (this.status === REJECTED) failCallback(this.reason)
}
}


// 测试当前 MyPromise

let promiseTest = new MyPromise((resolve, reject) => {
resolve('成功')
// reject('失败')
})
// 执行 then 方法
promiseTest.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})

2- Promise 对象的异步特性

  1. 异步特点就是不会阻塞代码执行,待异步结束后才会得到特定的值、特定的函数才会执行。
  2. 当执行 Promise 中的 then 方法时,Promise 对象的状态为 pending[等待状态时],就断定为异步状态。
  3. 断定为异步状态时,successCallback[成功回调]、failCallback[失败回调] 需要缓存在 Promise 对象中。
  4. 在构造函数内的异步方法执行完毕,调用 resolve[成功回调] 、reject[失败回调] 时,前置逻辑处理完毕后,最后执行外部调用 then 方法挂载至 Promise 对象中的 successCallbak[成功回调] 或 failCallback[失败回调]。
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
    
// Promise 的三个状态
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

/**
* 模拟 原生Promise
*
* @class MyPromise
* @constructor excutor 构造函数,函数接收俩个回调函数,回调函数
*/
     resolve = (value) => {
// 状态一旦确定不支持更改,return 出函数
if (this.status !== PENDING) return

// 更改状态为 fulfilled(成功)
this.status = FULFILLED

// 缓存成功回调 传递的值
this.value = value

// 执行成功回调
this.successCallback && this.successCallback(this.value)
}

/**
* 失败回调
*/
reject = (reason) => {
// 状态一旦确定不支持更改,return 出函数
if (this.status !== PENDING) return

// 更改状态为 rejected(失败)
this.status = REJECTED

// 缓存失败回调 传递的原因
this.reason = reason

// 执行失败回调
this.failCallback && this.failCallback(this.reason)
}

/**
* then 方法
*
* @params {function} successCallback[成功回调]
* @params {function} failCallback[失败回调]
*/
then ( successCallback, failCallback ) {
if (this.status === FULFILLED) successCallback(this.value)
else if (this.status === REJECTED) failCallback(this.reason)
else {
// 缓存成功回调
this.successCallback = successCallback
// 缓存失败回调
this.failCallback = failCallback
}
}
}


// 测试当前 MyPromise

let promiseTest = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
        // reject('失败')
    }, 2000)
})
promiseTest.then((value) => {
    console.log('成功回调:', value)
}, (reason) => {
    console.log('失败原因:', reason)
})


3- Promise 支持多次调用的特性实现

  1. 创建 Promise 方法是可以被多次调用的,在调用后每个 then 方法都应该被正确执行。
  2. 改造缓存回调函数的容器为数组,每次执行 then 方法后,successCallback[成功回调]、failCallback[失败回调] 都应进入对应的缓存栈。
  3. 在构造函数内调用 resolve[成功回调] 、reject[失败回调] 时,循环执行缓存的回调函数,并在回调函数内传入当前 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

// Promise 的三个状态
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败
/**
 *   模拟 原生Promise
 *  
 *   @class MyPromise
 *   @constructor excutor 构造函数,函数接收俩个回调函数,回调函数
 */
class MyPromise {
    constructor(excutor) {
        excutor(this.resolve, this.reject)
    }
    // 默认状态为 pending(等待)
    status = PENDING

    // 缓存状态值
    value = undefined // 成功状态 【参数】
    reason = undefined // 失败状态 【原因】
    // 缓存回调函数

    successCallback = [] // 成功回调
    failCallback = [] // 失败回调
    /**
     *   成功回调
     *
     *   @see 函数使用箭头函数,可以使 this 指向于当前 MyPromise 对象
     */
    resolve = (value) => {
        // 状态一旦确定不支持更改,return 出函数
        if (this.status !== PENDING) return

        // 更改状态为 fulfilled(成功)
        this.status = FULFILLED

        // 缓存成功回调 传递的值
        this.value = value

        // 执行成功回调
        while (this.successCallback.length) this.successCallback.shift()(this.value)
    }
    /**
     * 失败回调
     */
    reject = (reason) => {
        // 状态一旦确定不支持更改,return 出函数
        if (this.status !== PENDING) return

        // 更改状态为 rejected(失败)
        this.status = REJECTED

        // 缓存失败回调 传递的原因
        this.reason = reason

        // 执行失败回调
        while (this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    /**
     *   then 方法
     *
     *   @params {function} successCallback[成功回调]
     *   @params {function} failCallback[失败回调]
     */
    then(successCallback, failCallback) {
        if (this.status === FULFILLED) successCallback(this.value)
        else if (this.status === REJECTED) failCallback(this.reason)
        else {
            // 缓存成功回调
            this.successCallback.push(successCallback)
            // 缓存失败回调
            this.failCallback.push(failCallback)
        }
    }
}


// 测试当前 MyPromise

let promiseTest = (status, num) => {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            if (status) resolve(`成功:${num}`)
            else reject(`失败:${num}`)
        }, 2000)
    })
}
promiseTest(true, 1).then((value) => {
    console.log('成功回调:', value)
}, (reason) => {
    console.log('失败原因:', reason)
})
promiseTest(false, 2).then((value) => {
    console.log('成功回调:', value)
}, (reason) => {
    console.log('失败原因:', reason)
})
promiseTest(true, 3).then((value) => {
    console.log('成功回调:', value)
}, (reason) => {
    console.log('失败原因:', reason)
})

4- Promise 链式调用的特性实现

  1. 链式调用过程中,then 方法的 successCallback[成功回调]、failCallback[失败回调],是可选项。如果为空,则直接返回上一个 Promise 对象的返回值。
  2. Promise 对象是支持链式调用的,在执行 then 方法的时候,最终会返回一个新的 Promise 对象。
  3. 返回的对象,不支持返回当前调用的 Promise 对象。
  4. 返回的对象如果是对象,则获取 promise 的返回值,依据返回值调用 resolve、reject。
  5. 返回值是普通值则,调用 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
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

// Promise 的三个状态
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

/**
 *   模拟 原生Promise
 *  
 *   @class MyPromise
 *   @constructor excutor 构造函数,函数接收俩个回调函数,回调函数
 */
class MyPromise {
    constructor(excutor) {
        try {
            excutor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }

    // 默认状态为 pending(等待)
    status = PENDING

    // 缓存状态值
    value = undefined // 成功状态 【参数】
    reason = undefined // 失败状态 【原因】

    // 缓存回调函数
    successCallback = [] // 成功回调
    failCallback = [] // 失败回调

    /**
     *   成功回调
     *
     *   @see 函数使用箭头函数,可以使 this 指向于当前 MyPromise 对象
     */
    resolve = (value) => {
        // 状态一旦确定不支持更改,return 出函数
        if (this.status !== PENDING) return
        // 更改状态为 fulfilled(成功)
        this.status = FULFILLED
        // 缓存成功回调 传递的值
        this.value = value
        // 执行成功回调
        while (this.successCallback.length) this.successCallback.shift()()
    }

    /**
     * 失败回调
     */
    reject = (reason) => {
        // 状态一旦确定不支持更改,return 出函数
        if (this.status !== PENDING) return
        // 更改状态为 rejected(失败)
        this.status = REJECTED
        // 缓存失败回调 传递的原因
        this.reason = reason
        // 执行失败回调
        while (this.failCallback.length) this.failCallback.shift()()
    }

    /**
     *   then 方法
     *
     *   @params {function} successCallback[成功回调]
     *   @params {function} failCallback[失败回调]
     */
    then(successCallback, failCallback) {
        // 回调参数可以为空
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => { throw reason }

        const promiseNext = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 当前代码块改变为异步状态,获取到 promiseNext 对象
                setTimeout(() => {
                    try {
                        // 获取到回调结果
                        let successNext = successCallback(this.value)
                        // 执行返回逻辑
                        resolvePromise(promiseNext, successNext, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        // 获取到回调结果
                        let failNext = failCallback(this.reason)
                        // 执行返回逻辑
                        resolvePromise(promiseNext, failNext, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else {
                // 缓存成功回调
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 获取到回调结果
                            let successNext = successCallback(this.value)
                            // 执行返回逻辑
                            resolvePromise(promiseNext, successNext, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                // 缓存失败回调
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 获取到回调结果
                            let failNext = failCallback(this.reason)
                            // 执行返回逻辑
                            resolvePromise(promiseNext, failNext, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        // 链式调用,返回新的Promise
        return promiseNext
    }
}

// 链式调用处理回调函数
function resolvePromise(promise, next, resolve, reject) {
    // 禁止返回当前 Promise
    if (promise === next) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    // 判断当前返值状态
    if (next instanceof MyPromise) next.then(resolve, reject) // 如果 返回值为 Promise 对象,则获取到返回值,并调用 resolve、reject 方法传递
    else resolve(next) // 如果是普通值,则直接调用 resolve 方法
}


// 测试当前 MyPromise

let promiseTest = (status, num) => {
    return new MyPromise((resolve, reject) => {
        // setTimeout(() => {
            if (status) resolve(`成功:${num}`)
            else reject(`失败:${num}`)
        // }, 2000)
    })
}

// 链式调用
promiseTest(true, 1).then((value) => {
    console.log('成功回调:', value)
    throw Error(value.replace(/成功/, '失败').replace(/[0-9]/g, 2))
}, (reason) => {
    console.log('失败原因:', reason)
}).then((value) => {
    console.log('成功回调:', value)
    return value.replace(/[0-9]/g, 3)
}, (reason) => {
    console.log('失败原因:', reason)
    return (reason && reason.message || '失败:3').replace(/失败/, '失败 -> 成功').replace(/[0-9]/g, '2 -> 3')
}).then((value) => {
    console.log('成功回调:', value)
    return value.replace(/[0-9]/g, 4)
}, (reason) => {
    console.log('失败原因:', reason)
})

// 参数可选
promiseTest(true, 100).then().then().then((value) => console.log('value', value))
promiseTest(false, -100).then().then().then((value) => console.log(value), (reason) => console.log('reason', reason))

5- Promise 中的 all、resolve、finally、catch 方法

  • Promise 中的 all 方法

    1. all 方法是一 Promise 的静态方法,可以直接调用。
    2. 接收的参数为数组,数组中可以有普通值、同步方法、异步方法。
    3. 调用 all 方法后,会依次按顺序执行形参中的数组包含的值或方法。每个值或执行方法后,获取到的结果应按形参中的顺序缓存。
    4. all 方法最终的返回值是一个 Promise 对象。如果形参中的每个方法都得以正确执行,没有异常则调用 Promise 对象中的 resolve 方法,把最终结果缓存至返回的 Promise 对象中。如果有异常则把异常的原因,则调用 Promise 对象中的 reject 方法,缓存失败原因。
    5. 执行 then 方法就可以获取到,all 顺利完整执行的结果,或某一个状态失败的原因。
  • Promise 中的 resolve 方法

    1. resolve 方法也是一个 Promise 的静态方法,可以直接调用。
    2. resolve 方法接收一个参数,这个参数可以是普通值,也可以是异步方法。
    3. 如果接收到的值是普通值,则包装为 Promise 方法后,返回这个 Promise 方法。
    4. 如果接收到的值是异步方法,则直接返回这个方法。
  • Promise 中的 finally 方法

    1. finally 方法在 Promise 对象执行后,无论结果是 fulfilled 或 rejected,都会被执行。
    2. finally 返回值应为一个 Promise 对象,并且继承当前 Promise 对象的状态。
    3. finally 对象由于无法知道 Promise 的状态,所以回调函数函数不接收任何参数,仅用于无论 Promise 对象何种状态,都要执行的情况。
  • Promise 中的 catch 方法

    1. catch 方法会捕获 Promise 的失败状态。
    2. catch 方法内部为调用 then 方法,第一个参数,成功状态为空。

6- 完整的 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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

// Promise 的三个状态
const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

/**
 *   模拟 原生Promise
 *  
 *   @class MyPromise
 *   @constructor excutor 构造函数,函数接收俩个回调函数,回调函数
 */
class MyPromise {
    constructor(excutor) {
        try {
            excutor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }

    // 默认状态为 pending(等待)
    status = PENDING

    // 缓存状态值
    value = undefined // 成功状态 【参数】
    reason = undefined // 失败状态 【原因】

    // 缓存回调函数
    successCallback = [] // 成功回调
    failCallback = [] // 失败回调

    /**
     *   成功回调
     *
     *   @see 函数使用箭头函数,可以使 this 指向于当前 MyPromise 对象
     */
    resolve = (value) => {
        // 状态一旦确定不支持更改,return 出函数
        if (this.status !== PENDING) return
        // 更改状态为 fulfilled(成功)
        this.status = FULFILLED
        // 缓存成功回调 传递的值
        this.value = value
        // 执行成功回调
        while (this.successCallback.length) this.successCallback.shift()()
    }

    /**
     * 失败回调
     */
    reject = (reason) => {
        // 状态一旦确定不支持更改,return 出函数
        if (this.status !== PENDING) return
        // 更改状态为 rejected(失败)
        this.status = REJECTED
        // 缓存失败回调 传递的原因
        this.reason = reason
        // 执行失败回调
        while (this.failCallback.length) this.failCallback.shift()()
    }

    /**
     *   then 方法
     *
     *   @params {function} successCallback[成功回调]
     *   @params {function} failCallback[失败回调]
     */
    then(successCallback, failCallback) {
        // 回调参数可以为空
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => { throw reason }

        const promiseNext = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 当前代码块改变为异步状态,获取到 promiseNext 对象
                setTimeout(() => {
                    try {
                        // 获取到回调结果
                        let successNext = successCallback(this.value)
                        // 执行返回逻辑
                        resolvePromise(promiseNext, successNext, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        // 获取到回调结果
                        let failNext = failCallback(this.reason)
                        // 执行返回逻辑
                        resolvePromise(promiseNext, failNext, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            } else {
                // 缓存成功回调
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 获取到回调结果
                            let successNext = successCallback(this.value)
                            // 执行返回逻辑
                            resolvePromise(promiseNext, successNext, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                // 缓存失败回调
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 获取到回调结果
                            let failNext = failCallback(this.reason)
                            // 执行返回逻辑
                            resolvePromise(promiseNext, failNext, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        // 链式调用,返回新的Promise
        return promiseNext
    }

/**
* Promise 对象的 finally 方法
*/
finally(callback) {
// 调用 then 方法,获取 Promise 的状态,并返回
return this.then(
// 传递成功值,并把 回调函数包装为 Promise 对象 返回
(value) => MyPromise.resolve(callback()).then(() => value),
// 传递失败原因,并把 回调函数包装为 Promise 对象 返回
(reason) => MyPromise.resolve(callback().then(() => { throw reason })
))
}

/**
* Promise 对象的 then 方法
*/
catch(callback) {
return this.then(undefined, callback)
}


/**
* Promise 对象的 all 方法
*/
static all(array) {
// 缓存最终结果
let result = []
// 缓存结果的
let count = 0
// 返回 Promise 对象
return new MyPromise((resolve, reject) => {
function addData(key, value) {
// 按下标缓存最终值
result[key] = value

// 缓存值的次数
count++

// 缓存值的次数,等于 all 形参的长度,则代表所有方法或值都缓存至结果
if (count === array.length) resolve(result)
}

// 循环依次执行
for (let i = 0; i < array.length; i++) {
// 获取当前执行
let current = array[i]
// 异步方法处理
if (current instanceof MyPromise) {
current.then(value => addData(i, value), reason => resolve(reason))
} else { // 普通值处理
addData(i, current)
}
}
})
}

/**
* Promise 对象的 resolve 方法
*/
static resolve(value) {
if (value instanceof MyPromise) return value // Promise 对象则直接返回
else return new MyPromise(resolve => resolve(value)) // 普通值,则包装为异步方法
}
}

// 链式调用处理回调函数
function resolvePromise(promise, next, resolve, reject) {
    // 禁止返回当前 Promise
    if (promise === next) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    // 判断当前返值状态
    if (next instanceof MyPromise) next.then(resolve, reject) // 如果 返回值为 Promise 对象,则获取到返回值,并调用 resolve、reject 方法传递
    else resolve(next) // 如果是普通值,则直接调用 resolve 方法
}