11.手写封装 promise, 模拟 promise 源码2

2018-12-09  本文已影响0人  wudimingwo

丁老师学习名言
你用你会的知识去解释他, => 解释通了, 你就明白了
=> 解释不通, 该学习了!!

继续..

step1

constructor () {
...
用来存放then中的回调
this.resoveCB = null;
this.rejectCB = null;
}

...
then (resolveFn,rejectFn) {
...
              if (this.state == "pending") {
                return new myPromise((resolve, reject) => {
                  this.resolveCB = ((resolveFn)=>{
                    return () => {
                      var res = resolveFn(this.data);
                    }
                  })(resolveFn);
                })
              }

}

从这里开始就烧脑了,,,
首先我们需要在 'pending' 时先返回一个对象,
我们需要在 this.resolveCB中存一下函数resolveFn
但我们还需要把相应的参数也放进去 所以套了一层 函数绑定参数.

step2

              if (this.state == "pending") {
                return new myPromise((resolve, reject) => {
                  this.resolveCB = ((resolveFn)=>{
                    return () => {
                      var res = resolveFn(this.data);
                      if (res instanceof myPromise) {
                        res.then(resolve,reject);
                      } else{
                        resolve(res)
                      }
                    }
                  })(resolveFn);
                })
              }

刚开始确实是没看懂,
跟着执行顺序捋了一遍, 为什么这个逻辑成立是理解了.
因为pending 所以我们用this.resolveCB 来存了一下 函数,
返回了一个新的中间promise对象, 当前一个promise对象有了状态的变化时,
这个对象也要跟着改变对象,
如果有new了一个新的 new Promise对象, 就需要在这个new Promise对象的
状态改变时, 把中间的promise状态改变, 这样才能触发 保存的函数?
这里回头再思考

step3 补齐一下rejiect 这边

              if (this.state == "pending") {
                return new myPromise((resolve, reject) => {
                  this.resolveCB = ((resolveFn)=>{
                    return () => {
                      var res = resolveFn(this.data);
                      if (res instanceof myPromise) {
                        res.then(resolve,reject);
                      } else{
                        resolve(res)
                      }
                    }
                  })(resolveFn);
                  
                  this.rejectCB = ((rejectFn)=>{
                    return () => {
                      var res = rejectFn(this.data);
                      if (res instanceof myPromise) {
                        res.then(resolve,reject);
                      } else{
                        resolve(res)
                      }
                    }
                  })(rejectFn);
                })
              }

有一个地方我有点懵逼, 或者之前我理解有错误.
我以为promise 的后序then中 只要有一次进入 resolve 之后的都会进入resolve
有一次进入 reject 之后的都会进入rejiect
但老师写的reject 里 向下传递的状态是 resolve
也就是说 reject 线路还会回到 resolve?

测试

let P = new Promise((res,rej) => {
  
  rej(123)
}).then(null,(data) => {
  console.log(data);
  return 223
}).then(null,(data) => {
  console.log(data);
})// 按我之前的理解, 应该是返回223 但没有

let P = new Promise((res,rej) => {
  
  rej(123)
}).then(null,(data) => {
  console.log(data);
  return 223
}).then((data) => {
  console.log(data);
})// 返回了223

这要怎么理解呢?
也就是说默认then 执行过后返回的promise对象的状态都是 resolved
除非回调中返回的是新的 promise 对象.

step4
为了以下这种情况

let p = new myPromise((res,rej) => {
  rej(123)
})

p.then(null,(data) => {console.log(data + 200)});
p.then(null,(data) => {console.log(data)});
constructor(){
...

              this.resolveCB = [];
              this.rejectCB = [];
              
              let resolve = (data) => {
                if (this.state == "pending") {
                  this.state = "resolved"
                  this.data = data;
                  this.resolveCB.forEach(fn => fn());
                }
              }
              let reject = (data) => {
                if (this.state == "pending") {
                  this.state = "rejected"
                  this.data = data;
                  this.rejectCB.forEach(fn => fn());
                }
              }
}
...
then(resolveFn,rejectFn){
...

              if (this.state == "pending") {
                return new myPromise((resolve, reject) => {
                  this.resolveCB.push((resolveFn)=>{
                    return () => {
                      var res = resolveFn(this.data);
                      if (res instanceof myPromise) {
                        res.then(resolve,reject);
                      } else{
                        resolve(res)
                      }
                    }
                  })(resolveFn);
                  
                  this.rejectCB.push((rejectFn)=>{
                    return () => {
                      var res = rejectFn(this.data);
                      if (res instanceof myPromise) {
                        res.then(resolve,reject);
                      } else{
                        resolve(res)
                      }
                    }
                  })(rejectFn);
                })
              }
}

step5
让所有同步操作都变成异步?

              let resolve = (data) => {
                if (this.state == "pending") {
                  setTimeout(() => {
                    
                  this.state = "resolved"
                  this.data = data;
                  this.resolveCB.forEach(fn => fn());
                  },0)
                }
              }
              let reject = (data) => {
                if (this.state == "pending") {
                  setTimeout(() => {
                    
                  this.state = "rejected"
                  this.data = data;
                  this.rejectCB.forEach(fn => fn());
                  },0)
                }
              }

贴一下完整版

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

思考
实际上到昨天的同步处理为止,都是比较简单的.
到了要解决异步问题的时候,
我们发现必须要先返回一个对象, 并且要把函数先保存下来.
问题还在于, 我们要进行链式调用, 在明确每次then返回的对象都不相同时,
我们要解决一个问题就是,对象间的传递.
传递状态, 传递数据, 传递函数?
昨天我写得异常暴力版本,主要就是想不通怎么传递,
因为传递状态和数据的接口已经写好了, 理论上就应该用这两个接口
但这两个接口貌似只能在 new 新对象的时候才能传.

我们看一下就会发现, 丁老师的版本是怎么传的呢?
他确实是先返回了一个对象, 也是存了函数,
最核心的就是开篇讲的, 他在存函数的时候, 用函数的多层嵌套的方式,
把数据和函数一起绑定之后的函数放进了数组里.
然后在这个函数中调用各个接口,完成三个对象之间的状态,数据的传递.
当然我觉得我还是很难想到,但比刚开始好多了.

这应该是最近看到的代码中最漂亮的代码了.


发现可以简化一下, 如果把所有同步任务改成异步执行,
那then 可以不用判断 状态, 直接就是返回新对象

            class myPromise {
              constructor (fn) {
                
                this.state = "pending";
                this.data = null;
                
                this.rejectCB = [];
                this.resolveCB = [];
                
                let resolve = (data) => {
                  if (this.state == "pending") {
// 让同步任务变成异步执行
                    setTimeout(() => {
                      this.state = "resolved";
                      this.data = data;
                      this.resolveCB.forEach(item => {
                        item();
                      })
                    },0)
                  }
                }
                let reject = (data) => {
                  if (this.state == "pending") {
// 让同步任务变成异步执行
                    setTimeout(() => {
                      this.state = "rejected";
                      this.data = data;
                      this.rejectCB.forEach(item => {
                        item();
                      })
                    },0)
                  }
                }
                
                  fn(resolve,reject);
              }
              static resolve (data) {
                return new myPromise((suc) => {
                  suc(data);
                })
              }
              static reject (data) {
                return new myPromise((suc,fail) => {
                  fail(data);
                })
              }
              
              then (resolveFn,rejectFn) {
// 只需要保证 上面的异步执行晚于这个操作
// 那么这里可以不用判断状态, 统一返回中间对象
                    return new myPromise((res,rej) => {
                      this.resolveCB.push(() => {
                        let rus = resolveFn(this.data);
                        if (rus instanceof myPromise) {
                            rus.then(res,rej);
                        } else{
                            res(rus);
                        }
                      });
                      
                      this.rejectCB.push(() => {
                        let rus = rejectFn(this.data);
                        if (rus instanceof myPromise) {
                            rus.then(res,rej);
                        } else{
                            res(rus);
                        }
                      });
                    })
                
              }
              
            }

这里有个主意的地方,
我刚开始想, 如果要把同步改成异步执行,可以这样

            class myPromise {
              constructor (fn) {
                
                this.state = "pending";
                this.data = null;
                
                this.rejectCB = [];
                this.resolveCB = [];
                
                let resolve = (data) => {
                  if (this.state == "pending") {
                      this.state = "resolved";
                      this.data = data;
                      this.resolveCB.forEach(item => {
                        item();
                      })
                  }
                }
                let reject = (data) => {
                  if (this.state == "pending") {
                      this.state = "rejected";
                      this.data = data;
                      this.rejectCB.forEach(item => {
                        item();
                      })
                  }
                }
我刚开始以为,只要在这里改成异步,不是更简洁?
      >          setTimeout(() => {
      >          fn(resolve,reject);
      >        },0)
              }

但这是不行的, 之我们要改成异步的核心原因是,
resolveCB.push() 要比 resolveCB.forEach() 先执行.
如果改成上面这个样子.
resolveCB.push() 也会变成一个异步行为,
当执行异步任务的时候,
同为异步任务, resolveCB.forEach() 必然先执行.

想了大半天才理解这个道理.

总归来讲, promise 即使勉强会模拟了,
还是有很多地方不太理解.
你会发现整个接口都是提供回调函数的方式进行的.
如果按照我现在的思维方式,
我肯定下意识的想要提供一个mypromise.resolve() 这样对象调用接口.而不是回调的方式提供接口.
看来对函数应用的理解还是不够.
大牛们真是会玩.

上一篇下一篇

猜你喜欢

热点阅读