美文网首页
11.手写封装 promise, 模拟 promise 源码2

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

作者: wudimingwo | 来源:发表于2018-12-09 11:41 被阅读0次

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

    继续..

    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() 这样对象调用接口.而不是回调的方式提供接口.
    看来对函数应用的理解还是不够.
    大牛们真是会玩.

    相关文章

      网友评论

          本文标题:11.手写封装 promise, 模拟 promise 源码2

          本文链接:https://www.haomeiwen.com/subject/pxjhhqtx.html