Promise 原理分析与实现

时间:2021-1-8 作者:admin

promise 原理分析与实现

promise的构成和需要实现哪些东西

按照Promise/A+的标准来说,只需要Promise的then方法即可,至于怎么实现、constructor怎么写,是没有说的,所以这里使用es5或者es6来实现,其实不重要,如果为了兼容建议使用es5吧,只要注意其中几个重点。

  1. 首先,constructor是必须的,在这里我们需要做一些初始化的动作:

    1. 初始化状态
    2. 对传入的参数(resolver)进行类型判断,容错处理
    3. 执行传入的resolver函数
    4. 定义resolve和reject函数
  2. 其次,then 是在每一个对象都存在的,所以这里实现在原型链上。在then函数中,我们需要做的事情有:

    1. 对传入的onFulfilled、onRejected进行判断并做容错处理。
    2. 根据当前的Promise的状态做处理,然后返回一个新的Promise。
  3. 然后,我们还需要一个resolvePromise函数,这个函数根据标准而来(其实不管怎样,Promise的实现中都是少不了这样的处理过程)

至于原型上的catch方法以及race、all等静态方法,暂时不处理,而且这些在我们实现了Promise的基础代码后都是很容易的事情,暂时不做处理。

这里使用 ES6的class写法来实现,没什么,只是方便而已。

Constructor

|

 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

|

// 首先我们定义三种状态, 这三个状态是在整个Promise类的外层。
const PENDING  =  'pending'
const  FULFILLED  =  'fulfilled'
const  REJECTED  =  'rejected'

// 定义class
class MyPromise {
    constructor(resolver) {
        // 对resolver的类型进行判断
        if (typeof resolver !== 'function') {
            throw new TypeError('Promise resolver ' + resolver + 'is not a function')
        }
        this.status = PENDING // 初始化状态为 pending
        this.data = null // 初始化数据变量
        this.fulfilledCallbacks = [] // 当状态变成 fulfilled 时候,执行的队列函数
        this.rejectedCallbacks = [] // 当状态变成 rejected 时候,执行的队列函数

        resolve = (data) => {
            // 将回调放在下一个异步队列中执行
            setTimeout(() => { // 实际resolve的执行应该是 Microtasks ,这里使用异步队列模拟
              if (this.status !== PENDING) return // 因为promise的状态只能从pending -> fulfilled 或者 pending -> rejected, 所以这里做一个判断,如果不是 pending 就直接返回
              this.status = FULFILLED // 修改状态
              this.data = data
              let currentCallback
              // 执行所有的回调函数
              for (let i = 0; i < this.fulfilledCallbacks.length; i++) {
                currentCallback = this.fulfilledCallbacks[i]
                typeof currentCallback === 'function' && currentCallback(this.data)
              }
            }, 0)
        }
        // 与 resolve同理
        reject = (reason) => {
            setTimeout(() => {
              if (this.status !== PENDING) return
              this.status = REJECTED
              this.data = reason
              let currentCallback

              for (let i = 0; i < this.rejectedCallbacks.length; i++) {
                currentCallback = this.rejectedCallbacks[i]
                typeof currentCallback === 'function' && currentCallback(this.data)
              }
            }, 0)
         }

        try {
            resolver(resolve, reject)
        } catch (e) {
            reject(e)
        }

    }

    // ...
}

|

这个构造函数的作用就如上所说,就是初始化状态和定义两个关键的函数(resolve、reject),创建一个MyPromise对象,const myPromise = new MyPromise(function(resolve, reject){ ... }), function(resolve, reject){ ... }这个函数会在构造函数的最下面的try...catch中执行,然后将定义的resolve和reject传入。

|

1
2
3
4
5
6

|

try {
    resolver(resolve, reject) // function(resolve, reject){ ... }
} catch (e) {
    reject(e)
}

|

Promise.prototype.then

在实现最关键的then方法,由于没有个实例都是有then,所以这个方法应该放在原型上,根据ES6class的写法,直接在class中定义,就是挂着在prototype上的,详情可以可以看一下class这个语法糖的原理。

|

 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

|

const PENDING  =  'pending'
const  FULFILLED  =  'fulfilled'
const  REJECTED  =  'rejected'

class MyPromise {
    constructor(resolver) {
        ...
    }

    // 传入两个参数,一个是成功的回调,一个是错误处理
    // then(function(data) {}, function(err){})
    then(onFulfilled, onRejected) {
        const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (val) { return val }
        const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason }
        if (this.status !== PENDING) {
            return p = new MyPromise((resolve, reject) => {
                const funcCall = this.status === FULFILLED ? realOnFulfilled : realOnRejected
                try {
                    const rst = funcCall(this.data)
                    resolvePromise(p, rst, resolve, reject)
                } catch (error) {
                    reject(error)
                }
            })
        } else {
            return p = new MyPromise((resolve, reject) => {
              // 放入回调
              this.fulfilledCallbacks.push(() => {
                try {
                  const rst = onFulfilled(this.data)
                  resolvePromise(p, rst, resolve, reject)
                } catch (error) {
                  reject(error)
                }
              })

              this.rejectedCallbacks.push(() => {
                try {
                  const rst = onRejected(this.data)
                  resolvePromise(p, rst, resolve, reject)
                } catch (error) {
                  reject(error)
                }
              })
            })
          }
        }
}

|

可以看到then函数首先进行了参数兼容,然后又对状态进行了判断,再返回了一个新的Promise对象。接下来就来分析这里的代码。

  1. 对传入的两个参数,进行类型判断,如果不符合就使用默认的函数

    |

    1
    2
    3
    

    |

    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (val) { return val }
    const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason }
    

    |

  2. 声明一个变量,之后用来存放新的Promise对象

  3. 状态的判断,如果执行then的时候,当前对象的状态不是 pending 状态,则说明已经决议到了 fulfilled 或者 rejected状态,那么这个时候,就可以直接执行传入的 onFulfilled和onRejected函数了

    |

    1
    2
    3
    4
    

    |

    if (this.status !== PENDING) {
    
    }
    

    |

  4. promise的链式操作,所以这里必须返回一个新的MyPromise对象

    |

    1
    2
    3
    4
    

    |

    return p = new MyPromise((resolve, reject) => {
    //注意,promise构造函数时同步执行的,所以这里的代码会在这里直接执行,只是resolve函数会放在异步队栈,等同步执行完后,再去执行,当然ES6的Promise是放在了微任务队列,与异步队列还是有点区别的,这里只是模拟。
    })
    

    |

  5. 判断当前的状态是 fulfilled 还是 rejected状态,来决定是执行传入的 onFulfilled还是onRejected 函数

    |

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    

    |

    return p = new MyPromise((resolve, reject) => {
      const funcCall = this.status === FULFILLED ? realOnFulfilled : realOnRejected
      try {
          const rst = funcCall(this.data) // 把当前对象的数据丢进去执行
          resolvePromise(p, rst, resolve, reject)
      } catch (error) {
          reject(error)
      }
    })
    

    |

    this.data这个数据的由来是根据构造函数定义的resolvereject两个函数中赋值的,当这两个函数被调用了,那么this.data就有值了,then方法只是把这个值传入onFulfilled或者onRejected函数去执行,也就是交给我们来处理。 这里有一个resolvePromise(p, rst, resolve, reject)方法,这个resolvePromise是需要自己实现的,这个只是来判断把this.data带入onFulfilled或者onRejected函数执行后的返回值到底是什么类型,然后根据类型来判断该怎么改变状态和怎么执行。

  6. 状态的判断,如果当前的状态还是 pending 状态,那么就将 onFulfilled和onRejected函数放在相应的队列中,等待resolvereject被执行了,然后再执行这个两个队列。

    |

    1
    2
    3
    4
    5
    6
    7
    

    |

    else {
    // 还是返回一个promise对象,链式, 记住了。
        return p = new MyPromise((resolve, reject) => {
            ...
        })
    }
    

    |

  7. onFulfilled 放入 fulfilledCallbacks队列,等待执行

    |

    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    |

        this.fulfilledCallbacks.push(() => {
            try {
                const rst = onFulfilled(this.data)
                resolvePromise(p, rst, resolve, reject)
            } catch (error) {
                reject(error)
            }
        })
    

    |

  8. onRejected 放入 rejectedCallbacks队列,等待执行

    |

    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    |

        this.rejectedCallbacks.push(() => {
            try {
                const rst = onRejected(this.data)
                resolvePromise(p, rst, resolve, reject)
            } catch (error) {
                reject(error)
            }
        })
    

    |

整个then的逻辑就大致如上了。

resolvePromise的函数的实现

先上代码

|

 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

|

function resolvePromise(promise, data, resolve, reject) {
    let then, thenCalled
    if (promise === data) reject(new TypeError('Chaining cycle detected for promise!'))

    if (data instanceof MyPromise) { // 如果在then里面执行后,手动返回了一个Promise
      if (data.status === PENDING) {
        data.then((value) => {
          resolvePromise(promise, value, resolve, reject)
        }, reject)
      } else {
        data.then(resolve, reject)
      }
    } else if (data !== null && (typeof data === 'function' || typeof data === 'object')) { // 其他库的兼容
      try {
        then = data.then
        if (typeof then === 'function') {
          then.call(data, (value) => {
            if (thenCalled) return
            resolvePromise(promise, value, resolve, reject)
          }, (reason) => {
            if (thenCalled) return
            thenCalled = true
            return reject(reason)
          })
        } else {
          resolve(data)
        }
      } catch (error) {
        if (thenCalled) return
        thenCalled = true
        reject(error)
      }
    } else {
      resolve(data)
    }
  }

|

这里主要看这个函数最主要的就是if...else if...else 这三个分支了。

  1. if (data instanceof MyPromise):如果传入的data的值,是MyPromise的实例,也就是说,我们在onFulfilled里面有手动的返回了一个MyPromise示例,就比如下面的代码:

    |

    1
    2
    3
    4
    5
    

    |

    p.then((rst) => {
    ...
    return new MyPromise((resolve, reject) => { ... }) // 手动的返回了一个 MyPromise
    })
    

    |

    在回想一下,then函数最后是会返回一个MyPromise实例的,但是这里,这个函数是传入then的第一个参数onFulfilled, 这里的判断是如果onFulfilled又返回了一个MyPromise实例,不要弄混了。

    |

    1
    2
    3
    4
    5
    6
    

    |

    // 传入then的参数onFulfilled: 
    (rst) => {
    ...
    return new MyPromise((resolve, reject) => { ... }) // 手动的返回了一个 MyPromise
    }
    

    |

    根据这个判断,既然data又是一个MyPromise,所以就直接执行data.then了:

        if (data.status === PENDING) { // 判断一下这个 `MyPromis`的状态, 如果封装的好一点,这个状态应该不能这样直接被访问,可以弄一个获取状态的get函数。
            data.then((value) => {
            resolvePromise(promise, value, resolve, reject) // 递归调用resolvePromise,注意这里是当前这个Promise决议到了 fulfilled 状态才会去在执行 resolvePromise,这样就能链式起来了
            }, reject)
        } else { // 如果已经决议了,那就直接把resolve和reject传进去,执行了,然后再走一遍这个MyPromise的then方法
            data.then(resolve, reject)
        }
    
  2. else if (data !== null && (typeof data === 'function' || typeof data === 'object')): 这个判断主要是为了兼容其他Promise的库。

    1. 尽量去执行 datathen函数:

    |

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    

    |

        try {
            then = data.then
            if (typeof then === 'function') {
                then.call(data, (value) => { // 当前的 data 传入其他Promise库去执性
                    if (thenCalled) return
                        resolvePromise(promise, value, resolve, reject)
                    }, (reason) => {
                    if (thenCalled) return
                    thenCalled = true // 被标记过 then 已经执行过了
                    return reject(reason)
                })
            } else { // 如果没有then,就直接把data放入resolve执行了
                resolve(data)
            }
        } catch (error) {
            // reject的执行逻辑
            if (thenCalled) return
            thenCalled = true
            reject(error)
        }
    

    |

  3. 第三种情况,其他,就直接执行 resolve了。

catch 方法

先上代码

|

1
2
3
4
5
6
7
8

|

catch(onRejected) {
    const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason }
    let p = new Promise((resolve, reject) => {
        let x = realOnRejected(this.data);
        resolvePromise(promise2, x, resolve, reject);
    });
}

|

其实 catch 方法只是 reject 执行逻辑而已,这里就把 then 里面对于 reject 的逻辑拿来用就行。不过这里没有返回一个Promise了,因为已经报错了么,如果想实现finally,这里是需要返回的。

最后

其实对于Promise关键就是这些了,理解原理,除了问题也是比较好定位的。关于Promise容易吞掉错误的情况还是需要注意。其他的Promise上的一些静态函数allrace等以后再看看吧。

全部的源码

|

  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

|

// myPromise.js
const MyPromise = (function () {
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'

  function resolvePromise(promise, data, resolve, reject) {
    let then, thenCalled
    if (promise === data) reject(new TypeError('Chaining cycle detected for promise!'))

    if (data instanceof MyPromise) { // 如果在then里面执行后,手动返回了一个Promise
      if (data.status === PENDING) {
        data.then((value) => {
          resolvePromise(promise, value, resolve, reject)
        }, reject)
      } else {
        data.then(resolve, reject)
      }
    } else if (data !== null && (typeof data === 'function' || typeof data === 'object')) { // 其他库的兼容
      try {
        then = data.then
        if (typeof then === 'function') {
          then.call(data, (value) => {
            if (thenCalled) return
            resolvePromise(promise, value, resolve, reject)
          }, (reason) => {
            if (thenCalled) return
            thenCalled = true
            return reject(reason)
          })
        } else {
          resolve(data)
        }
      } catch (error) {
        if (thenCalled) return
        thenCalled = true
        reject(error)
      }
    } else {
      resolve(data)
    }
  }

  return class MyPromise {
    constructor(resolver) {
      if (typeof resolver !== 'function') {
        throw new TypeError('Promise resolver ' + resolver + 'is not a function')
      }
      this.status = PENDING
      this.fulfilledCallbacks = []
      this.rejectedCallbacks = []
      this.data = null

      const onFulfilled = (data) => {
        setTimeout(() => {
          if (this.status !== PENDING) return
          this.status = FULFILLED
          this.data = data
          let currentCallback

          for (let i = 0; i < this.fulfilledCallbacks.length; i++) {
            currentCallback = this.fulfilledCallbacks[i]
            typeof currentCallback === 'function' && currentCallback(this.data)
          }
        }, 0)
      }

      const onRejected = (reason) => {
        setTimeout(() => {
          if (this.status !== PENDING) return
          this.status = REJECTED
          this.data = reason
          let currentCallback

          for (let i = 0; i < this.rejectedCallbacks.length; i++) {
            currentCallback = this.rejectedCallbacks[i]
            typeof currentCallback === 'function' && currentCallback(this.data)
          }
        }, 0)
      }

      try {
        resolver(onFulfilled, onRejected)
      } catch (e) {
        onRejected(e)
      }

    }

    then(onFulfilled, onRejected) {
      let p
      const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (val) { return val }
      const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason }

      if (this.status !== PENDING) { // 立即执行
        return p = new MyPromise((resolve, reject) => {
          const funcCall = this.status === FULFILLED ? realOnFulfilled : realOnRejected
          try {
            const rst = funcCall(this.data)
            resolvePromise(p, rst, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else {
        return p = new MyPromise((resolve, reject) => {
          // 放入回调
          this.fulfilledCallbacks.push(() => {
            try {
              const rst = onFulfilled(this.data)
              resolvePromise(p, rst, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })

          this.rejectedCallbacks.push(() => {
            try {
              const rst = onRejected(this.data)
              resolvePromise(p, rst, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    }

    catch(onRejected) {
      const realOnRejected = typeof onRejected === 'function' ? onRejected : function (reason) { throw reason }
      let p = new Promise((resolve, reject) => {
        let x = realOnRejected(this.data);
        resolvePromise(promise2, x, resolve, reject);
      });
    }

  }
})()

|

参考文章

  1. www.zenoven.com/technology/…
  2. zhuanlan.zhihu.com/p/32988235
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。