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

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

继续..

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,698评论 1 56
  • title: promise总结 总结在前 前言 下文类似 Promise#then、Promise#resolv...
    JyLie阅读 12,215评论 1 21
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 817评论 0 2
  • 前言 本文旨在简单讲解一下javascript中的Promise对象的概念,特性与简单的使用方法。并在文末会附上一...
    _暮雨清秋_阅读 2,180评论 0 3
  • 越来越多的朋友喜欢为客厅挂置一幅装饰画,既可以提升客厅装饰的品位,同时又能为客厅带来好的风水寓意,不过选择客厅装饰...
    戈瑞家居阅读 436评论 0 0