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

加载图片,用promise

            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
            let p = new Promise((res,rej) => {
              res();
              rej();
          
            })
image.png

可以看出

  • promise 状态是不可逆的.
            let p = new Promise((res,rej) => {
              res('suc');
              rej('err');
            })
                
            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

原来如此, 我还在想呢, 如果每次都要 new Promise, 那then 的链式调用也没太大意思
想要链式调用,要解决两个东西, 一个是状态的改变, 一个是数据的传递.
then里执行过后, 默认下个then状态都会变成 resolve
猜测, p 的两个回调 ,如果执行了第一个, 就会触发 p1 的第一个回调

  • then方法会返回promise对象, 对应回调函数中的return 作为Promise对象中的data
            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');})
imag.png

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

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

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

            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)
            })
            
            let P = Promise.all([p1,p2,p3]);
            P.then((data) => {console.log(data);},(data) => {console.log(data);})
image.png

只有当三个全为 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);})
            
image.png

可知, 只要有一个 触发 reject 就会触发,并且只返回第一个reject的p的数据
执行时间取决于第一个触发 reject的promise对象.

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

这里的并行异步请求 指的是 各个请求之间没有顺序上的依赖.
如果存在顺序上的依赖, 明显用then 比较合适.


Promise.race([p1,p2,p3])

            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);})
            

可知
Promise.race() 求的是三个请求中最快的那个,
不关心 请求的结果如何.(这样就没什么太大意思啊)
通常用于 网络测试?


Promise.prototype.catch()

            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

表明执行了 then(null,()=>{})之后, 就不会再执行catch 了?


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

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

        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 模拟状态不可逆

              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

            static resolve (data) {
              return new myPromise((suc) => {
                suc(data);
              })
            }
            static reject (data) {
             return new myPromise((suc,err) => {
              err(data);
            })
            }

step4 模拟 then

            then (resolveFn, rejectFn) {
              if (this.state == "resolved") {
                resolveFn(this.data);
              }
              if (this.state == "rejected") {
                rejectFn(this.data);
              }
            }

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

            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 对象,
这里是稍微难的, 感觉很巧妙.不容易想到.

            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.resolve(rus);
              }
              }
            } 

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

        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.resolve(rus);
              }
              }
            } 
            static resolve (data) {
              return new myPromise((suc) => {
                suc(data);
              })
            }
            static reject (data) {
             return new myPromise((suc,err) => {
              err(data);
            })
            }
        }

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

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

      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

      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))

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

      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的, 只不过方法稍微暴力了一点.
不对是非常的暴力, 感觉非常不可取, 但我们先写一下,,

      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))

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

明天再看视频.

你可能感兴趣的:(10.手写封装 promise, 模拟 promise 源码1)