Promise实现及原理

前言

  本文主要用自己语言表达出Promise 的基本实现及原理

基本实现

  先看一个简单的 promise 用法

 1 new Promise((resolve, reject) => {
 2     console.log(1)
 3 
 4     setTimeout(() => {
 5         console.log(2)
 6         resolve()
 7     }, 2000)
 8 
 9 }).then(() => {
10 
11     console.log(3)
12 
13 })
14 
15 //打印
16 // 1
17 // 2 18 // 3

  1:运行该代码后,打印出 1 2 3,说明 Promise 构造传进去的函数是由 Promise 调用的,then 里的函数也是由 Promise 调用的,并且两者是有先后关系的,先调用了 Promise 构造里的方法,再调用 then 里的方法。

  2:把上面代码第 6 行代码注释掉或删掉,代码最终运行打印为 1 2,then 里的方法并没有运行,说明 构造的回调参数 resolve 与 reject 是保证在Promise中同步运行的关键,主动调用了 resolve 后,才调用了 then 里的方法。

    由此,可以大概得出下面代码:

 1 function MyPromise(fn){
 2     this.fns = null
 3 
 4     fn(this._resolve.bind(this), this._reject.bind(this))
 5 }
 6 MyPromise.prototype = {
 7     constructor: MyPromise,
 8 
 9     then: function(thenfn) {
10         this.fns = thenfn
11     },
12 
13     _resolve: function() {
14         if (this.fns) this.fns()
15     },
16 
17     _reject: function() {
18 
19     }
20 }
21 
22 new MyPromise((resolve, reject) => {
23     console.log(1)
24     setTimeout(() => {
25         console.log(2)
26         resolve()
27     }, 2000)
28 }).then(() => {
29     console.log(3)
30 })

  运行上面代码,同样可以打印出 1 2 3,主要原理是运行 Promise 构造里的回调后,将 then 方法的回调运行的主动权交出去了( _resolve ),而 then 方法的回调已经暂存在 MyPromise 里了(then 方法的职责就是将回调暂存在一个变量里),这样就能保证在 Promise 里的异步执行完成后才执行 then 的回调。

  

 

改造

  • 问题1

  then 只能调用一次,每次调用,then 的回调都会被覆盖:

 1 function MyPromise(fn){
 2     this.fns = []
 3 
 4     fn(this._resolve.bind(this), this._reject.bind(this))
 5 }
 6 MyPromise.prototype = {
 7     constructor: MyPromise,
 8 
 9     then: function(thenfn) {
10         this.fns.push(thenfn)
11     },
12 
13     _resolve: function() {
14         this.fns.forEach(v => v())
15     },
16 
17     _reject: function() {
18 
19     }
20 }
21 
22 let a = new MyPromise((resolve, reject) => {
23     console.log(1)
24     setTimeout(() => {
25         console.log(2)
26         resolve()
27     }, 2000)
28 })
29 a.then(() => {
30     console.log(3)
31 })
32 a.then(() => {
33     console.log(4)
34 })

  解决方法其实就是使用数组把 then 回调暂存起来,使之能够暂存多个 then 的回调。执行时再遍历执行 then 回调。

 

  • 问题2

  如果 Promise 里的回调执行的不是异步,而是同步的话,then 无法被执行。

1 new MyPromise((resolve, reject) => {
2     console.log(1)
3     resolve()
4 }).then(() => {
5     console.log(3)
6 })

  如上代码,只打印了 1,其根本原因在于执行 resolve 的时候,then 的回调还没有被添加到 fns 里:

 1 function MyPromise(fn){
 2     this.fns = []
 3     this.state = 'pending'
 4 
 5 
 6     fn(this._resolve.bind(this), this._reject.bind(this))
 7 }
 8 MyPromise.prototype = {
 9     constructor: MyPromise,
10 
11     then: function(thenfn) {
12         if (this.state == 'pending') {
13             this.fns.push(thenfn)
14         } else {
15             thenfn()
16         }
17         
18     },
19 
20     _resolve: function() {
21         this.state = 'fulfilled'
22         this.fns.forEach(v => v())
23     },
24 
25     _reject: function() {
26 
27     }
28 }
29 
30 new MyPromise((resolve, reject) => {
31     console.log(1)
32     resolve()
33 }).then(() => {
34     console.log(3)
35 })

  执行上面代码,就能正确打印出 1 3 了,增加一个状态,初始为 pending ,执行 resolve 时,将状态改变为 fulfilled ,而在 then 添加回调到 fns 时,判断当前状态是否是 pending,不是 pending ,说明构造的回调已经执行完了,则直接执行 then 的回调,而不是添加到 fns 等待执行。

 

  • 问题3

  缺少参数传递,要能够在执行异步后将结果传递给 then 的回调,使之能够根据结果进行下一步操作:

 1 function MyPromise(fn){
 2     this.fns = []
 3     this.state = 'pending'
 4     this.value = null
 5 
 6     fn(this._resolve.bind(this), this._reject.bind(this))
 7 }
 8 MyPromise.prototype = {
 9     constructor: MyPromise,
10 
11     then: function(thenfn) {
12         if (this.state == 'pending') {
13             this.fns.push(thenfn)
14         } else {
15             thenfn(this.value)
16         }
17         
18     },
19 
20     _resolve: function(value) {
21         this.state = 'fulfilled'
22         this.value = value
23         this.fns.forEach(v => v(value))
24     },
25 
26     _reject: function() {
27 
28     }
29 }
30 
31 let a = new MyPromise((resolve, reject) => {
32     console.log(1)
33     resolve('传递数据')
34 })
35 
36 a.then((msg) => {
37     console.log(msg, 2)
38 })
39 a.then((msg) => {
40     console.log(msg, 3)
41 })

  resolve 接收数据,而在执行 resolve 时,直接将数据传递给 then 的回调。

 

  • 问题4

  如果有多个 then ,then 的回调接收的数据都是一样的:

 1 let a = new MyPromise((resolve, reject) => {
 2     console.log(1)
 3     resolve('传递数据')
 4 })
 5 
 6 a.then((msg) => {
 7     console.log(msg, 2)
 8 })
 9 a.then((msg) => {
10     console.log(msg, 3)
11 })

  以上代码运行后,打印 1 传递数据 2 传递数据 3 ,回调接收的数据都是一样。如果 new 两个不同的 Promise 呢:

 1 let a = new MyPromise((resolve, reject) => {
 2     console.log(1)
 3     resolve('传递数据a')
 4 })
 5 
 6 let b = new MyPromise((resolve, reject) => {
 7     console.log(1)
 8     resolve('传递数据b')
 9 })
10 
11 a.then((msg) => {
12     console.log(msg)
13 })
14 b.then((msg) => {
15     console.log(msg)
16 })

  如上,虽然 a 和 b 的 then 的回调接收的数据不一样了,但两者没有衔接起来,需要 b 能够接收到 a 的数据,从而根据 a 的数据做出其他操作,并且如果 a 和 b 都执行的是异步的话,两者的顺序不能保证。

 1 function MyPromise(fn){
 2     this.fns = []
 3     this.state = 'pending'
 4     this.value = null
 5 
 6     fn(this._resolve.bind(this), this._reject.bind(this))
 7 }
 8 MyPromise.prototype = {
 9     constructor: MyPromise,
10 
11     then: function(thenfn) {
12         return new MyPromise ((resolve, reject) => {
13 
14             this._handle({
15                 onFulfilled: thenfn,
16                 resolve: resolve
17             })
18 
19         })
20     },
21 
22     _handle: function(callback) {
23         if (this.state == 'pending') {
24             this.fns.push(callback)
25             return
26         }
27         
28         if (!callback.onFulfilled) {
29             callback.resolve(this.value)
30             return
31         }
32 
33         let res = callback.onFulfilled(this.value)
34         callback.resolve(res)
35 
36     },
37 
38     _resolve: function(value) {
39         this.state = 'fulfilled'
40         this.value = value
41         this.fns.forEach(v => this._handle(v))
42     },
43 
44     _reject: function() {
45 
46     }
47 }
48 
49 new MyPromise((resolve, reject) => {
50     resolve(1)
51 }).then((msg) => {
52     console.log(msg)
53     return ++msg
54 }).then((msg) => {
55     console.log(msg)
  56     msg++
  57     console.log(msg)
56 })

  运行上面代码,打印 1 2 3,第一个 then 回调接收到数据后进行 + 1,然后传递给 第二个 then 的回调,使第二个 then 的回调能够根据第一个 then 的回调结果进行操作。

  其主要在于 then 返回了一个新的 Promise,使每次 then 都是一个新的 Promise 对象,而每个新的 Promise 对象中也只暂存了一个 then 的回调,每个新的 Promise 对象里的 value 也保存着各自执行 resolve 传递的数据。

  在上面 33,34 行中,将 then 的回调执行的结果传递给下一个 Promise 对象的 resolve,才能够使多个 then 进行衔接。

  虽然在 then 中 new 了新的 Promise 对象,但在执行 _handle 时,其 this 是上一个 Promise 对象的,而其中 callback.onFulfilled 和 callback.resolve 是 then 中的( 是下一个Promise对象的 ),这也是多个 then 能够衔接的关键。

 

  • 问题5

  如果 then 的回调返回的也是 Promise 的类型呢

 1 new MyPromise((resolve, reject) => {
 2     resolve(1)
 3 }).then((msg) => {
 4     console.log(msg)
 5     return new MyPromise((resolve, reject) => {
 6         resolve(2)
 7     })
 8 }).then((msg) => {
 9     console.log(msg)
10 })

  如上,打印结果为 1 和 MyPromise ,并没有如期打印出 1 和 2。这里很好解决,既然返回的是 Promise ,那么我们主动调用它的 then 即可,其实也类似 多个 then 的链式调用。

 1 _resolve: function(value) {
 2         
 3         if(value && (typeof value === 'object' && value instanceof MyPromise)) {
 4             if (typeof value.then === 'function') {
 5                 value.then.call(value, this._resolve.bind(this), this._reject.bind(this))
 6                 return
 7             }
 8         }
 9 
10         this.state = 'fulfilled'
11         this.value = value
12         this.fns.forEach(v => this._handle(v))
13     }

  这里只修改了 _resolve,判断返回的是否是 MyPromise,是则主动调用 MyPromise 的 then。

  then 中返回 Promise ,而在 _resolve 中主动调用 Promise 的 then,其原理跟调用时的多个 .then 一样,只不过返回 Promise 的是在里层主动调用罢了。

 

  • 添加 reject 处理

 1 function MyPromise(fn){
 2     this.fns = []
 3     this.state = 'pending'
 4     this.value = null
 5 
 6     fn(this._resolve.bind(this), this._reject.bind(this))
 7 }
 8 MyPromise.prototype = {
 9     constructor: MyPromise,
10 
11     then: function(onFulfilled, onRejected) {
12         return new MyPromise ((resolve, reject) => {
13 
14             this._handle({
15                 onFulfilled: onFulfilled,
16                 onRejected: onRejected,
17                 resolve: resolve,
18                 reject: reject
19             })
20 
21         })
22     },
23 
24     _handle: function(callback) {
25 
26         if (this.state == 'pending') {
27             this.fns.push(callback)
28             return
29         }
30 
31         let cb = this.state == 'fulfilled' ? callback.onFulfilled : callback.onRejected
32         
33         if (!cb) {
34             cb = this.state == 'fulfilled' ? callback.resolve : callback.reject
35             cb(this.value)
36             return
37         }
38 
39 
40         let res = cb(this.value)
41         cb = this.state == 'fulfilled' ? callback.resolve : callback.reject
42         cb(res)
43 
44     },
45 
46     _resolve: function(value) {
47 
48         if(value && (typeof value === 'object' && value instanceof MyPromise)) {
49             if (typeof value.then === 'function') {
50                 value.then.call(value, this._resolve.bind(this), this._reject.bind(this))
51                 return
52             }
53         }
54 
55         this.state = 'fulfilled'
56         this.value = value
57         this.fns.forEach(v => this._handle(v))
58     },
59 
60     _reject: function(err) {
61 
62         this.state = 'rejected'
63         this.value = err
64         this.fns.forEach(v => this._handle(v))
65 
66     }
67 }

  添加一个 reject ,无非再增加一种状态,然后再在 _handle 中增加状态的判断,以运行 resolve 或者 reject。

 

  • 添加 catch

 1 _handle: function(callback) {
 2 
 3         if (this.state == 'pending') {
 4             this.fns.push(callback)
 5             return
 6         }
 7 
 8         let cb = this.state == 'fulfilled' ? callback.onFulfilled : callback.onRejected
 9         
10         if (!cb) {
11             cb = this.state == 'fulfilled' ? callback.resolve : callback.reject
12             cb(this.value)
13             return
14         }
15 
16         let res
17         try{
18             res = cb(this.value)
19             cb = this.state == 'fulfilled' ? callback.resolve : callback.reject
20         } catch(err) {
21             res = err
22             cb = callback.reject
23         } finally {
24             cb(res)
25         }
26         
27 
28     }
1 catch: function(err) {
2         return this.then(null, err)
3     }

  添加 catch 则在运行回调时进行 try/catch 的捕获,以执行 resolve 或 reject,而 catch 方法其实就是执行一个 失败的 then,所以直接返回 then(null, err)

  

 完整代码

 1 function MyPromise(fn){
 2     this.fns = []
 3     this.state = 'pending'
 4     this.value = null
 5 
 6     fn(this._resolve.bind(this), this._reject.bind(this))
 7 }
 8 MyPromise.prototype = {
 9     constructor: MyPromise,
10 
11     then: function(onFulfilled, onRejected) {
12         return new MyPromise ((resolve, reject) => {
13 
14             this._handle({
15                 onFulfilled: onFulfilled,
16                 onRejected: onRejected,
17                 resolve: resolve,
18                 reject: reject
19             })
20 
21         })
22     },
23 
24     catch: function(err) {
25         return this.then(null, err)
26     },
27 
28     _handle: function(callback) {
29 
30         if (this.state == 'pending') {
31             this.fns.push(callback)
32             return
33         }
34 
35         let cb = this.state == 'fulfilled' ? callback.onFulfilled : callback.onRejected
36         
37         if (!cb) {
38             cb = this.state == 'fulfilled' ? callback.resolve : callback.reject
39             cb(this.value)
40             return
41         }
42 
43         let res
44         try{
45             res = cb(this.value)
46             cb = this.state == 'fulfilled' ? callback.resolve : callback.reject
47         } catch(err) {
48             res = err
49             cb = callback.reject
50         } finally {
51             cb(res)
52         }
53         
54 
55     },
56 
57     _resolve: function(value) {
58 
59         if(value && (typeof value === 'object' && value instanceof MyPromise)) {
60             if (typeof value.then === 'function') {
61                 value.then.call(value, this._resolve.bind(this), this._reject.bind(this))
62                 return
63             }
64         }
65 
66         this.state = 'fulfilled'
67         this.value = value
68         this.fns.forEach(v => this._handle(v))
69     },
70 
71     _reject: function(err) {
72 
73         this.state = 'rejected'
74         this.value = err
75         this.fns.forEach(v => this._handle(v))
76 
77     }
78 }
View Code

 

参考

  图解 Promise 实现原理(一)—— 基础实现 - 知乎 (zhihu.com)

posted @ 2021-06-12 17:07  blogCblog  阅读(530)  评论(1编辑  收藏  举报