10.手写封装 promise, 模拟 promise 源码1

"Hello World, Hello Blog"

Posted by wudimingwo on December 15, 2018

加载图片,用promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
            function loadURL (url) {
              let img = new Image();
            	return new Promise((resolve,reject) => {
            	  
            	  img.onload = function () {
            	  	resolve(img);
                    /11111
            	  }
            	  img.onerror = function () {
            	    reject();
            	  	throw `${url} is nor correct`
            	  }
            	  img.src = url
            	})
            }

resolve(img) 有两个作用, 一个是 改变状态,声明状态 另一个是将返回值传递出去.


promise细致用法 image.png

1
2
3
4
5
            let p = new Promise((res,rej) => {
              res();
              rej();
          
            })

image.png

可以看出

  • promise 状态是不可逆的. ``` let p = new Promise((res,rej) => { res(‘suc’); rej(‘err’); })
1
2
        let p1 = p.then((data) => {console.log(data,'p');},(data) => {console.log(data,'p');});
                 p1.then((data) => {console.log(data,'p1');},(data) => {console.log(data,'p1');}) ``` ![image.png](https://upload-images.jianshu.io/upload_images/13637909-a9cb0891debc0965.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) > 原来如此, 我还在想呢, 如果每次都要 new Promise, 那then 的链式调用也没太大意思 > 想要链式调用,要解决两个东西, 一个是状态的改变, 一个是数据的传递. > 状态似乎是启用哪个回调,就会默认是相应的状态 > 猜测, p 的两个回调 ,如果执行了第一个, 就会触发 p1 的第一个回调 > * then方法会返回promise对象, 对应回调函数中的return 作为Promise对象中的data
1
2
3
4
5
6
7
            let p = new Promise((res,rej) => {
              res('suc');
              rej('err');
            })
                
            let p1 = p.then((data) => {console.log(data,'p');return "sucsuc"},(data) => {console.log(data,'p');return "errerr"});
                     p1.then((data) => {console.log(data,'p1');},(data) => {console.log(data,'p1');})

image.png

如此我们可以理解为 当第一次new Promise() 执行时, 返回的状态, 会决定后序的链全是 resolve,或者是 reject? 不会出现中途从resolve 转到 reject? 除非 中间重新new?

整理一下

  • Promise 对象的状态的是不可逆的
  • P.then()执行完后会返回一个promise对象, 我们称为 PP吧
  • 如果P.then() 的回调函数返回的是非 promise 对象, PP的状态由回调函数是哪个来决定, PP.then()的回调函数的参数就是 P的回调函数返回的值 如果P.then() 回调函数返回的是promise对象, 也就是在P.then() 的回调函数中new 了一个 Promise() PP的状态和数据就由 new 时来重新决定.
  • 这整理的简直不想看第二遍…呵呵.. 也许应该画图. 或者看代码.

image.png

如果回调中 又new了一个 则状态又由new时 的resolve/reject 来决定 错了错了, 画了半天的图, 都是错的. 默认 then()执行过后状态都会变成resolve, 也就是说,刚开始进入了 reject回调, 但执行过后 下一个promise的对象还是resolved 回头还是删了这个图吧.


Promise.resolve() 返回一个带有明确data且状态为 rosolved 的 Promise对象.

1
2
            let p = Promise.resolve(123);
            p.then((data) => {console.log(data);})/123

Promise.reject() 返回一个带有明确data且状态为 rejected的 Promise对象.

1
2
            let p = Promise.reject(123);
            p.then(()=>{},(data) => {console.log(data);})/123

Promise.all([p1,p2,p3]) ``` let p1 = Promise.resolve(111); let p2 = new Promise((res,rej) => { setTimeout(function () { res(222) },1000) }) let p3 = new Promise((res,rej) => { setTimeout(function () { res(333) },2000) })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        let P = Promise.all([p1,p2,p3]);
        P.then((data) => {console.log(data);},(data) => {console.log(data);}) ``` ![image.png](https://upload-images.jianshu.io/upload_images/13637909-bf54441321103a4e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) > 只有当三个全为 resolved 时, 才会触发  > 执行时间取决于最后一个 执行resolve的promise对象 ```
        let p1 = Promise.resolve(111);
        let p2 = new Promise((res,rej) => {
          setTimeout(function () {
          	rej(222)
          },1000)
        })
        let p3 = new Promise((res,rej) => {
          setTimeout(function () {
          	rej(333)
          },2000)
        })
        
        let P = Promise.all([p1,p2,p3]);
        P.then((data) => {console.log(data);},(data) => {console.log(data);})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
![image.png](https://upload-images.jianshu.io/upload_images/13637909-43bb018403433856.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
> 可知, 只要有一个 触发 reject 就会触发,并且只返回第一个reject的p的数据
> 执行时间取决于第一个触发 rejuect的promise对象.

> Promise.all() 的用处
> 比如, 我要请求三次数据, 我可以每次获取数据都渲染一次页面,(3次)
> 也可以三个数据都获得之后 再渲染一次页面(1次)
> 但只要有一个数据是无法获取, 就要渲染一个 error提示页面
> 这个时候用 Promise.all() 就很适合.
> 类似的, 对于管理多个并行异步请求, 似乎是比较适合的.
> 并且这几个请求缺一不可. 

> 这里的并行异步请求 指的是 各个请求之间没有顺序上的依赖.
> 如果存在顺序上的依赖, 明显用then 比较合适.
--------------------
> Promise.race([p1,p2,p3])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        let p1 = new Promise((res,rej) => {
          setTimeout(function () {
          	res(111)
          },3000)
        })
        let p2 = new Promise((res,rej) => {
          setTimeout(function () {
          	rej(222)
          },1000)
        })
        let p3 = new Promise((res,rej) => {
          setTimeout(function () {
          	rej(333)
          },2000)
        })
        
        let P = Promise.race([p1,p2,p3]);
        P.then((data) => {console.log(data);},(data) => {console.log(data);})
1
2
3
4
5
6
7
> 可知
> Promise.race() 求的是三个请求中最快的那个,
> 不关心 请求的结果如何.(这样就没什么太大意思啊)
> 通常用于 网络测试?

-----------------------------------------
Promise.prototype.catch()
1
2
3
4
5
6
7
8
9
10
11
12
        Promise.resolve(1).catch((data) => {console.log(data)});/ 没有触发
        Promise.reject(1).catch((data) => {console.log(data)}); / 返回1 ``` > catch()是 用来代替 p.then(null,(data)=>{}) 的? ```
        new Promise((res,rej) => {
          rej('err')
        }).then((data) => {console.log(data)}).catch((data) => {console.log(data)})/ err ``` ```
        
        new Promise((res,rej) => {
          rej('err')
        }).then((data) => {console.log(data)},data => {
          console.log(123);
          return data;
        }).catch((data) => {console.log(data)}) ``` ![image.png](https://upload-images.jianshu.io/upload_images/13637909-80df79146054fb1c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) > 表明执行了 then(null,()=>{})之后, 就不会再执行catch 了?

为什么不看源码? 因为看不懂 代码的28法则 一个完整的代码总是包含很多辅助性的代码 这些辅助性的代码的作用是 增强代码的稳定性,健壮性,安全性 这些辅助性的代码 会让代码变得很难阅读 核心代码占据 20% 辅助性代码占据80%

模拟封装promise step 1 模拟参数校验, 以及基本架构

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 (fn) {
        	  if (typeof fn !== "function") {
        	  	throw TypeError(`myPromise resolver ${fn} is not a function`)
        	  }
        	  this.state = "pending";
        	  this.data = undefined;
        	  
        	  let resolve = (data) => {
        	    this.state = "resolved"
        	    this.data = data;
        	  }
        	  let reject = (data) => {
        	    this.state = "rejected"
        	    this.data = data;
        	  }
        	  
        	  fn(resolve,reject);
        	}
        	then () {
        	  
        	}
        	static resolve () {
        	  
        	}
        	static reject () {
        	  
        	}
        }
        
        new myPromise(123);

step2 模拟状态不可逆

1
2
3
4
5
6
7
8
9
10
11
12
        	  let resolve = (data) => {
        	    if (this.state == "pending") {
        	      this.state = "resolved"
        	      this.data = data;
        	    }
        	  }
        	  let reject = (data) => {
        	    if (this.state == "pending") {
        	      this.state = "rejected"
        	      this.data = data;
        	    }
        	  }

step3 模拟 Promise.resolve,Promise.reject

1
2
3
4
5
6
7
8
9
10
        	static resolve (data) {
        	  return new myPromise((suc) => {
        	    suc(data);
        	  })
        	}
        	static reject (data) {
        	 return new myPromise((suc,err) => {
              err(data);
            })
        	}

step4 模拟 then

1
2
3
4
5
6
7
8
        	then (resolveFn, rejectFn) {
        	  if (this.state == "resolved") {
        	  	resolveFn(this.data);
        	  }
        	  if (this.state == "rejected") {
        	  	rejectFn(this.data);
        	  }
        	}

step5 判断回调的返回值是否为 promise 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
        	then (resolveFn, rejectFn) {
        	  if (this.state == "resolved") {
        	  	let rus = resolveFn(this.data);
        	  	if (rus instanceof myPromise) {
        	  		console.log('is a promise');
        	  	}else{
        	  	  console.log(" not a promise");
        	  	}
        	  }
        	  if (this.state == "rejected") {
        	  	rejectFn(this.data);
        	  }
        	}

step6 根据判断 返回不同的 promise 对象, 这里是稍微难的, 感觉很巧妙.不容易想到.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        	then (resolveFn, rejectFn) {
        	  if (this.state == "resolved") {
        	  	let rus = resolveFn(this.data);
        	  	if (rus instanceof myPromise) {
                          // 返回新 promise 对象 状态 数据都由 new时定义
        	  	  return rus
        	  	}else{
                          // 也是返回新promise 对象, 但状态确定为 resolved, 数据则为 rus, 这个数据储存在这个新对象的 this.data中, 便于这个对象的then调用
        	  	  return myPromise.resolve(rus);
        	  	}
        	  }
        	  if (this.state == "rejected") {
        	  	let rus = rejectFn(this.data);
        	  	if (rus instanceof myPromise) {
                return rus
              }else{
                return myPromise.reject(rus);
              }
        	  }
        	} 

到此时,先贴一下整体代码

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
        class myPromise {
        	constructor (fn) {
        	  if (typeof fn !== "function") {
        	  	throw TypeError(`myPromise resolver ${fn} is not a function`)
        	  }
        	  this.state = "pending";
        	  this.data = undefined;
        	  
        	  let resolve = (data) => {
        	    if (this.state == "pending") {
        	      this.state = "resolved"
        	      this.data = data;
        	    }
        	  }
        	  let reject = (data) => {
        	    if (this.state == "pending") {
        	      this.state = "rejected"
        	      this.data = data;
        	    }
        	  }
        	  
        	  fn(resolve,reject);
        	}
        	then (resolveFn, rejectFn) {
        	  if (this.state == "resolved") {
        	  	let rus = resolveFn(this.data);
        	  	if (rus instanceof myPromise) {
        	  	  return rus
        	  	}else{
        	  	  return myPromise.resolve(rus);
        	  	}
        	  }
        	  if (this.state == "rejected") {
        	  	let rus = rejectFn(this.data);
        	  	if (rus instanceof myPromise) {
                return rus
              }else{
                return myPromise.reject(rus);
              }
        	  }
        	} 
        	static resolve (data) {
        	  return new myPromise((suc) => {
        	    suc(data);
        	  })
        	}
        	static reject (data) {
        	 return new myPromise((suc,err) => {
              err(data);
            })
        	}
        }

不过无法解决 异步的问题.

作业1, 用 原生写一遍以上代码

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
      function myPromise(fn) {
        if(typeof fn !== "function") {
          throw TypeError(`${fn} is not a function`)
        }

        this.state = "pending";
        this.data = undefined;
        
        
        // 这个地方不用箭头函数, 就需要把 this用变量存一下
        let _this = this;
        let resolve = function(data) {
          if(_this.state == "pending") {
            _this.state = "resolved";
            _this.data = data;
          }
        }
        let reject = function(data) {
          if(_this.state == "pending") {
            _this.state = "rejected";
            _this.data = data;
          }
        }

        fn(resolve, reject);

      }

      myPromise.resolve = function(data) {
        return new myPromise((res) => {
          res(data);
        })
      }
      myPromise.reject = function(data) {
        return new myPromise((res,rej) => {
          rej(data);
        })
      }
      myPromise.prototype.then = function(resolveFn, rejectFn) {
        if (this.state == "resolved") {
        	let rus = resolveFn(this.data);
        	if (rus instanceof myPromise) {
        		return rus
        	}else{
        	  return myPromise.resolve(rus);
        	}
        }
        if (this.state == "rejected") {
        	let rus = rejectFn(this.data);
        	if (rus instanceof myPromise) {
        		return rus
        	}else {
        	  return myPromise.reject(rus);
        	}
        }
      }

作业2 思考一下如何解决异步问题, 也就是 state == ‘pending’时该怎么办?

什么时候resolve 执行, 改变了状态, 什么时候就执行 then 里面的回调函数. 也就是说, then里的函数,应该是先进行注册,进行保存, 当 状态发生变化时, 相应保存的函数再执行. 并且执行完之后,再返回 promise对象. then是链式调用,每个then不能马上返回promise对象, 那样会出问题.

所以关键是要存这个函数. 然后再考虑怎么返回promise对象, 因为还要把注册的不同的函数, 分配给不同的promise 对象?

step1

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
      class myPromise {
        constructor(fn) {
          if(typeof fn !== "function") {
            throw TypeError(`myPromise resolver ${fn} is not a function`)
          }
          this.state = "pending";
          this.data = undefined;
          // 用来储存 resolveFn的数组
          this.resList = [];
          // 用来储存 rejectFn的数组
          this.rejList = [];
          
          let resolve = (data) => {
            if(this.state == "pending") {
              this.state = "resolved"
              this.data = data;
              if (this.resList.length > 0) {
                this.resList.shift()();
              }
            }
          }
          let reject = (data) => {
            if(this.state == "pending") {
              this.state = "rejected"
              this.data = data;
              if (this.rejList.length > 0) {
                this.rejList.shift()();
              }
            }
          }
          
          fn(resolve, reject);
        }
        
        then(resolveFn, rejectFn) {
          
          this.resList.push( () => {
          	let rus = resolveFn(this.data);
            if(rus instanceof myPromise) {
              return rus
            } else {
              return myPromise.resolve(rus);
            }
          })
            
            this.rejList.push(() => {
            	let rus = rejectFn(this.data);
            if(rus instanceof myPromise) {
              return rus
            } else {
              return myPromise.reject(rus);
            }
            })
        }
        static resolve(data) {
          return new myPromise((suc) => {
            suc(data);
          })
        }
        static reject(data) {
          return new myPromise((suc, err) => {
            err(data);
          })
        }
      }

      let p = new myPromise((suc, err) => {
        setTimeout(function () {
        	err('suc');
        },2000);
      });

      p.then(null, (data) => console.log(data))

这样处理,能够完成第一步的异步, 但一旦链式调用, 则会报错

1
      p.then(null, (data) => console.log(data)).then(null,data => console.log(data))

image.png 这很正常, 因为 我们的then在执行第一次后没有返回任何东西. 我们应该返回一个临时promise对象?,你不能返回跟原来的一样的, 这个promise对象只需要做一件事情, 就是把 then里的回调都注册进来. 问题是, 我们是不是应该把所有的回调都储存在同一个数组? 应该, 因为我们还要分出去, 所以这个数组不能放在实例上, 而应该放在原型上, 这样每个promise对象,都可以注册函数. 所以问题变成了, state==’pending’ 时, 我们要返回什么promise对象? 这个对象并不需要设置 resolve,reject, 状态, 假设我们随便返回一个promise对象, 在原型的数组上注册了相应的函数, 但我们怎么去寻找呢? 应该是每个promise对象的 resolve/reject 对应相应的注册的函数. 根据对象的什么进行查找呢? 根据顺序嘛? 但这个对象和函数之间有可能不是一对一的关系, 而是一对多的关系. 所以这个数组应该是个二维数组?

假设我们在 state == ‘pending’时, 返回了一个对象, 然后没有在原型上的数组中进行注册,而是在这个实例上进行了注册. 那我们要做的就是在then执行之后返回对象时,或者传递数据的时候, 只要在这个对象上操作就可以了?

怎么确定下一个promise对象是哪一个? 第一个思路是还是在 原型上定义一个数组,专门用来存放生成的promise对象, 然后按顺序执行即可. 但这么做似乎不妥,不妥在于,同一个promise对象可以并行执行多个then(), 返回的promise对象,应该都是不同的, 那这些对象是放在原型的同一个数组嘛?

第二个思路是,链表的思路, 我只需要在这个实例上标记好,我要返回的下一个实例是谁就可以了.

我们试一下? 虽然还是一团乱麻, 但可以试一下, 对了还有一个问题, 这个问题是, 按照上面这个逻辑讲, 实例对象应该在 then() 的时候先产生, 然后在resolve的时候返回. 可如果是同步代码,这就会出问题. 同步代码是, resolve 会比then先进行读取,因为,我们一般会把then写在后面. 解决方法是, 我们用 setTimeout() 让 resolve 运行缓一下?

一敲代码就发现问题了, 上面的思路行不通, 行不通的原因是, 说到底, 我是想在then时先创建对象, 然后在resolve时 返回对象. 问题在于, 数据必须在创建对象的时候传递, 而数据只有在resolve的时候才会得到. 也就是说 then时我生成了对象,但无法把数据传进去

不对,严格来讲还是可以改变data的, 只不过方法稍微暴力了一点. 不对是非常的暴力, 感觉非常不可取, 但我们先写一下,,

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
      class myPromise {
        constructor(fn) {
          if(typeof fn !== "function") {
            throw TypeError(`myPromise resolver ${fn} is not a function`)
          }
          this.state = "pending";
          this.data = undefined;
          
用来存放注册的函数
          this.resList = [];
          this.rejList = [];
          
          let resolve = (data) => {
            if(this.state == "pending") {
              this.state = "resolved"
              this.data = data;
 如果有注册的函数就执行注册的函数, 注意,这里要用传参的方式把数据传进去
              this.resList.forEach((item) => {
                item(this.data);
              })
            }
          }
          let reject = (data) => {
            if(this.state == "pending") {
              this.state = "rejected"
              this.data = data;
如果有注册的函数就执行注册的函数, 注意,这里要用传参的方式把数据传进去
              this.rejList.forEach((item) => {
                item(this.data);
              })
              
              
            }
          }
          这里是为了能够让 then在resolve,reject之前执行.
          主要是解决同步时候的问题.
          setTimeout(function () {
            fn(resolve, reject);
          },0)
        }
        
        then(resolveFn, rejectFn) {
          把函数放进相应的数组里.
          this.resList.push( (data) => {
         这里不能用 this.data 因为我们会有一次更换对象的操作.
          	let rus = resolveFn(data);
            if(rus instanceof myPromise) {
          从这里开始就惨不忍睹了, 非常暴力
          把之前生成的对象里的 注册函数 暴力赋值
              rus.resList = this.next.resList;
              rus.next = this.next.next;
              return rus
            } else {
          用暴力的赋值的方式更改对象的状态和 数据
              this.next.state = "resolved";
              this.next.data = rus;
              this.next.resList.forEach((item) => {
                item(rus);
              })
            }
          })
            
            this.rejList.push((data) => {
            	let rus = rejectFn(data);
            if(rus instanceof myPromise) {
              rus.rejList = this.next.rejList;
              rus.next = this.next.next;
              return rus
            } else {
              this.next.state = "rejected";
              this.next.data = rus;
              this.next.rejList.forEach((item) => {
                item(rus);
              })
            }
            })
            这里我们先生成了一个promise对象, 并且返回了出去.
            this.next = new myPromise((resolve,reject) => {});
            return this.next;
        }
        static resolve(data) {
          return new myPromise((suc) => {
            suc(data);
          })
        }
        static reject(data) {
          return new myPromise((suc, err) => {
            err(data);
          })
        }
      }

      let p = new myPromise((suc, err) => {
        setTimeout(function () {
        	err('suc');
        },2000);
      });

      p.then(null, (data) => {console.log(data);
        return new myPromise((res,rej) => {
          setTimeout(function () {
          	rej('err');
          },1000)
        })
        
      }).then(null,data => {console.log(data);return 223})
      .then(null,data => console.log(data))

貌似整个能够模拟今天所学的内容. 但异常暴力,感觉问题很大很大.. 不过思考两个小时,能够坚持思考,并敲一敲,还是挺欣慰的.

明天再看视频.