手写简单promise和断点式分析

简单 promise 的实现

看过不少种 promise 的实现,一直想本身写一个,吭哧了好久,终于写出来了(先不写 all 等方法和 reject 方法,后期在 issues 中补充),就拿它做为第一篇博客吧promise

大纲

  1. 先罗列一下可能不全的 promiseA+规范
  2. 逆推,从 promise 使用到实现
  3. 实现思路和步骤代码
  4. 总体代码夹注释
  5. 断点式分析

promiseA+ 规范(从别人文章里看到的,可能不全)

  1. promise 须要是一个对象或者函数
  2. promise 须要有三个状态 pending,fulfilled,rejected,状态变化只能是 pending 到后二者之一,且不可逆
  3. promise 必须有一个 then 方法,该方法接收两个函数参数 onFunfilled 和 onRejected。前者为 pending 到 fulfilled 时执行的方法,后者为 pending 到 rejected 的方法。
  4. then 方法必须返回一个新的 promise
  5. 确保 then 里面的函数异步执行

promise 的使用

  • 代码浏览器

    let proB = new PromiseB((resolve) => { console.log('promiseB: resolve'); resolve('then1') })
    proB.then(value => {
      console.log('then1', value)
      return 'then2'
    }).then(//建立pro3
    data => {
      return new PromiseB((resolve) => {//是pro4
        setTimeout(() => {
          console.log('非正常then2:', data)
          resolve('then3')
        }, 1000)
      })
    }).then(value => {
      console.log('数据then3', value)
      return 'then4'
    }).then(value => {
      console.log('数据then4', value)
      return 'then5'
    }).then().then(
      value => { console.log('then5:', value) }
    )
    复制代码
  • 本身实现执行结果: 异步

    selfPromiseResult

  • promise 执行的结果: async

    promiseResult

实现思路

  • 先看一下使用中须要注意的方面函数

    1. promise 的函数参数是当即执行的,参数为 resolve,由此开启 promise 逻辑
    2. then 的两个函数参数有多是同步执行,也有多是异步执行(注:这里分两种状况判断区分异步同步使用 state==='pending'便可
    3. then 可能没有参数
    4. then 参数函数中 return 的数据下一个 then 也得能拿到才能够
  • 具体实现思路ui

    1. 先讲一个概念,链式执行,我在下面一直说同步异步,其实就是当 onFulfilled 返回是一个 promise 的状况下,其后面的调用都应该是在该参数 promise 的 resolve 触发之后才继续执行,而这个 resolve 通常都是在一个异步环境中触发,定时器,接口清求等,全部后面的步骤就有了‘等待’这个过程。
    2. 首先建立一个名为 promiseB 的类接收两个函数参数,该类有一个 then 方法,有一个 state 属性,then 方法接收两个函数参数数 onFunfilled 和 onRejected,then 返回一个新的 promise
    class PromiseB{
      constructor(excutor){
      this.state='pending'// 'pending'|'fulfilled'|'redected'
      excutor(this.resolve)
      }
      then=(onFunfilled,onRejected)=>{//class properties语法,目前谷歌浏览器已经支持了
      return new PromiseB(/*这里应该有一个函数参数*/)
      }
    
    }
    复制代码
    1. 上面代码中 classProperties 语法就是就是会自动绑定该方法到实例上,至关于 class A{constructor(){this.handleA=this.handleA.bind(this)};handuleA(){console.log('who am I belong to:',this)}} onFunfilled 函数参数的参数是 resolve 的数据或者其余 then 的返回值 then 返回的 promise 的参数下面讲
    2. 除了上述须要的数据之外,excutor 的参数为 resolve 方法,还须要 一个 value 变量 来存储每次 onFulfilled 函数接收的参数值。为了知足异步调用的状况,还须要有一个状态去存储须要延后执行的 onFulfilled 函数(一开始准备叫 callbacks,后来发现不贴切,在断点分析中会讲缘由),就叫 asyncObj 吧。
    class PromiseB{
      constructor(excutor) {
       this.state='pending'// 'pending'|'fulfilled'|'redected'
       this.value = null
       this.asyncObj = {}
       excutor(this.resolve)
     }
     then=(onFunfilled,onRejected)=>{//class properties语法,目前谷歌浏览器已经支持了
       return new PromiseB(/*这里应该有一个函数参数*/)
     }
     resolve = (newValue) => {}
    }
    复制代码
    1. 接下来就是实现 then 和 resolve 两个方法,先讲 then 方法,其实主要是讲 then 返回的内部 promise 的逻辑。 then 方法执行的时候分为同步和异步,即该 promise 的 then 触发的时候,其参数 onfulfilled 的参数 value 可否拿到正确的值,即该 promise resolve 的参数。咱们经过 this.state==='pending'来辅助判断,this.state 的修改只能在 resolve 中进行,若是 this.pendin==='pending'为真,那么认为是异步的,先在还不能执行 onFulfilled,讲其存在该 promise 的 asyncObj 中,若是 this.state='pending'为假,那么认为咱们已经能够拿到正确的值,为同步,执行 onFulfilled。同时为了保证 promise 能够继续链式调用下去,而且下一个 promise 能够拿到正确值,咱们须要在异步时把本次 then 返回的新 promise 的 resolve 存起来,同步则把 onFulfilled(this.value)的结果经过新 promise.resolve 传递出去,若是该 then 没有参数,即没有 onfulfilled,直接 resolve 掉 this.value。至于为什么 promise.resolve 能够把数据传递给下一个 promise,咱们在 resolve 和断点分析中都会讲。下面是 then 的实现,将上述描述转化成代码:
    then = (onFUlfilled) => {
        return new PromiseB(resolve => {
          if (this.state === "pending") {
            this.asyncObj={ onFUlfilled, resolve };
          } else {
            if(!onFUlfilled)return resolve(this.value)
            resolve(onFUlfilled(this.value));
          }
        });
      };
    复制代码
    1. 实现 resolve 同步方法,这里应该会有一行代码让你至关迷惑,我也是断点调试才完全搞清楚的 resolve 是在新建立的 promise 中能够被使用者显式调用的,同时它也是在 then 隐式返回的 promise 中隐式调用的。resolve 首先要接收一个参数 value,而后结合上面 then 讲到的,在 resolve 中要改变 promise 的状态。代码实现(resolve 和 then 同级)
    resolve = newValue => {
        this.state = "fulfilled";
        this.value = newValue;
    };
    复制代码
    1. 实现 resolve 同步和异步方法,上述代码执行同步的代码是没问题了,可是有一个问题,那就是异步的状况下怎么办?结合面 then 方法,then 方法中若是状态没有改变会把 onfullfiled 和新返回的 promise 的 resolve 存到 asyncObj 中,resolve 固然是在正确的时间节点下把他们取出来调用一番啦。至于这个正确的时间节点其实就是当参数 promise 的 resolve 触发之后下一步执行便可。代码至于怎么实现其实就是首先遇见异步或者说 onFulfilled 返回了一个 promise,更确切一点就是当某一个 promise 的 resolve 方法的参数是一个 promise 的时候,第一 不要去修改这个 promise 的 value,由于咱们想要的 value 是参数 promise 调 resolve 返回的值,而不是这个 promise 自己,第二 不要去修改状态,只要不修改状态,后续执行的全部的 then 都会将 onFulfilled 和新的 resolve 存储到该 promise 的 asyncObj 中而不是去执行它,这就实现了等待的功能,以及保留了之后链式执行的顺序。第三 当时间节点合适的时候取出 asynObj 中的方法,执行他们。第四,也是最重要的一步,若是 resolve 的参数 value 是一个 promise,执行这个参数 promise 的 then 方法,而且将本 promise 的 resolve 做为 onfulfilled 传递进去。上述说的时间节点其实就是 resolve 的参数是否是 promise。讲了一堆应该仍是没有描述清楚,下面的断点分析会完全讲清楚,下面是转述代码
    resolve = newValue => {
        if (newValue && newValue.then) {
          newValue.then(this.resolve);
        } else {
          this.state = "fulfilled";
          this.value = newValue;
          if(!asyncObj.onFUlfilled && asyncObj.resolve)return asyncObj.resolve(this.value)
          asyncObj.resolve(asyncObj.onFUlfilled(this.value))
        }
      };
    复制代码
  • 所有代码实现:this

    class PromiseB {
      constructor(excutor) {
        this.state = "pending";
        this.value = null;
        this.asyncObj = {};
        excutor(this.resolve);
      }
      then = onFulfilled =>
        new PromiseB(resolve => {
          if (this.state === "pending")
            return (this.asyncObj = { onFulfilled, resolve });
          if (!onFulfilled) return resolve(this.value);
          resolve(onFulfilled(this.value));
        });
      resolve=value=>{
        if(value&&value.then){
          value.then(this.resolve)
        }else{
          this.state='fulfilled'
          this.value=value
          setTimeout(() => {
              const {onFulfilled,resolve} = this.asyncObj
              if(onFulfilled&&resolve)resolve(onFulfilled(this.value))
              if(!onFulfilled&&resolve)resolve(this.value)
            },
          0);
        }
      }
    }
    复制代码

上面留下几个坑

  1. resolve 为何能够保存链式结构而且让异步之后的代码执行下去?

    由于在参数不为 promise 而且 asyncObj 有值的状况下,看第 23 行代码,这里实际上是一个递归,onFulfiled 其实就是该 promise 的 then 的参数,resolve 是下个 prommsie 的 resolve。实现了递归遍历数据。spa

  2. 为何 asyncObj 不叫 callbacks,若是看过其余实现的话,你会发现,大部分人都会叫 callbacks。

    其实通常来讲,若是真的看懂了上面的代码是不会有这个疑问的,这里并不算是发布订阅, 通常咱们发布订阅都是存一系列的 callback,一次 emit,执行全部回调,咱们这里是递归,每次执行的时候只有一个以前存好的 onFulfilled 和一个 resolve调试

  3. 参数 promise 的 onfulfilled 为何是 this.resolve?

    这个其实得留到断点分析里去讲,由于篇幅有点长,并且我本身感受也没有彻底搞清楚,我是逆推分析出来的。code

分析

其实为了搞懂上面那些东西,我用了差很少三天的时间,最后经过编号和打断点才搞清楚地。其实就几个问题,若是都能搞清楚,那你应该对 promise 没有任何疑问了。

答案在文中,结果在底部

  1. 使用示例中总共有几个 promise?
  2. 使用示例中总共执行了几回 then,顺序是怎样的?
  3. 使用示例中总共执行了几回 resolve,顺序是怎样的?

断点分析

最好先想一下上面的问题,若是答案不对或者没有搞很清楚,带着疑问往下看,收获会更大。生成的第几个promise就记作pro几,例如第一个promise就叫pro1。pro1的onfulfilled就是pro1的then的参数onfulfilled.

  1. 第一个promise为pro1,执行 excutor(resolve),即执行 console.log('promiseB: resolve’) ,并触发 pro1.resolve('then1'),步骤1结束
  2. pro1.resolve('then1')触发, 判断传入的值是否为promise,值'then1'不是promise:pro1.value改变为pro1.resolve参数'then1',pro1状态改变,执行setTimeout里的代码,由于asyncObj为空,其实啥都不会执行,步骤2结束。
  3. pro1.resolve代码块结束之后,示例代码第二行 proB.then... 执行,拉开了promise chain的序幕。 pro1.then(onFulfilled) 触发,返回新的promise pro2 ,判断pro1的状态,经过:执行Pro1的onFulfilled-> value=>{console.log('then1', value)return 'then2'},执行完毕之后获取到返回值'then2',将'then2'做为pro2.resolve的参数,即执行 pro2.resolve('then2') ,步骤3结束
  4. pro2.resolve('then2')触发,判断传入的值是否为promise,值'then2'不是promise:pro2.value改变为pro2.resolve参数‘then2’,pro2状态改变,执行setTimeout里的代码,由于asyncObj为空,其实啥都不会执行,步骤4结束。
  5. pro2.then(onFulfilled) 触发,返回新的promise pro3 ,判断pro2的状态,经过,执行pro2的then的onfulfilled->data => { return new PromiseB((resolve) => {setTimeout(() => { console.log('非正常then2:', data) resolve('then3') }, 1000) })},建立并执行pro4的excutor(resolve),setTimeout(() => { console.log('非正常then2:', data) resolve('then3') }, 1000) }),这个resolve是pro4的,获得返回值 一个promise即pro4 ,将返回值做为value传递给pro3的resolve,执行pro3.resolve(pro4),步骤5结束
  6. pro3.resolve(pro4) 触发,判断传入的值是否为promise,值 pro4 是promise:执行 pro4.then(pro3.resolve),步骤6结束。
  7. pro4.then(pro3.resolve)触发,返回新的promise pro5,判断pro4的状态,由于状态的改变都是在this.resolve执行且参数不是promise的状况下才会改变,而pro4.resolve尚未执行,因此 不经过 :将 参数promise即pro4onfulfilled即pro3的resolvepro5的resolve 存起来,存到 参数promise即pro4的asyncObj 中,即此时 pro4.asyncObj={onfulfilled:pro3.resolve,resolve:pro5.resolve}步骤7结束,执行完毕之后继续回到promise chain,此时应该执行的是 pro3.then(onFulfilled)
  8. pro3.then(onFulfilled)触发(我以前的思路一直是卡在步骤7结束)。返回新的promise pro6 ,判断pro3的状态,由步骤6可知,不经过:将pro3的onFulfilled-> (value => {console.log('数据then3', value)return 'then4'}) 和pro6的resolve存到pro3的asyncObj中,即此时pro3.asyncObj={onfulfilled:pro3.onFulfilled,resolve:pro6.resolve} ,返回pro6,执行 pro6.then(onFulfilled) 步骤8结束。
  9. pro6.then(onFulfilled)触发,返回新的promise pro7 ,判断pro6的状态,和步骤7中pro4对于状态的判断一致,不经过:将pro6的onfulfilled->value=>{console.log('数据then4', value)return 'then5'}和pro7的resolve存到pro6的asyncObj中,即此时 pro6.asyncObj={onfulfilled:pro6.onFulfilled,resolve:pro7.resolve},返回pro7,执行pro7.then(onFulfilled),步骤9结束
  10. pro7.then(onFulfilled)触发,返回新的promise pro8 ,判断pro7的状态,和步骤7中pro4对于状态的判断一致,不经过:将pro7的onfulfilled->undefined和pro8的resolve存到pro7的asyncObj中,即此时 pro7.asyncObj={onfulfilled:pro7.onFulfilled,resolve:pro8.resolve},返回pro8,执行pro8.then(onFulfilled),步骤10结束
  11. pro8.then(onFulfilled)触发,返回新的promise pro9 ,判断pro8的状态,和步骤7中pro4对于状态的判断一致,不经过:将pro8的onfulfilled->value => {console.log('then5:', value) }和pro9的resolve存到pro8的asyncObj中,即此时 pro8.asyncObj={onfulfilled:pro8.onFulfilled,resolve:pro9.resolve},返回pro9,由于pro9没有then,因此 没有建立新的pro10 ,promise chain的then或者说同步过程结束,执行promise chain 之后的逻辑,直到参数promise,即pro4的resolve被触发,才继续执行promise chain,步骤11结束。
  12. 致此,全部的then都触发了一遍,执行完成之后总共有 6个显式then(每一个then都会建立新的promise)+1个初始promise+1个参数promise+1个隐式then(参数promise的then会隐式执行建立一个新的promise)=9个promise,从上面步骤可得 then的执行顺序为:1,2,4,3,6,7,8 异步以前的前两个promise已经执行完毕, 当前执行的 resolve状况为:1,2,3 ,从参数promise即pro4,触发之后,后面逻辑中的 promise.asyncObj中都存储了本promise的onfulfilled和该then返回的新promise的resolve。须要特别提醒一下的是 pro3的asyncObj中存储的是pro的onfulfiled和pro6的resolve,由于pro3.then建立的是pro6 。同时,pro7的asyncObj中存的onfulfilled是undefined。定时器完成,pro4.resolve('then3')进入队列,等待主栈逻辑执行完毕即触发,步骤12结束
  13. pro4.resolve('then3')触发,判断传入的值是否为promise,值'then3'不是promise:pro4.value改变为pro4.resolve参数'then3',pro4状态改变,执行setTimeout里的代码,由步骤7可知 pro4.asyncObj={onfulfilled:pro3.resolve,resolve:pro5.resolve}先执行 let nnvalue = onfulfilled('then3'),获得结果之后执行 pro5.resolve(nnvalue),onfulfilled('then3')触发,onfulfiled为pro3.resolve,即执行 pro3.resolve('then3'),可是步骤13其实尚未结束,由于pro5.resolve(nnvalue)尚未执行。
  14. pro3.resolve('then3')触发,判断传入的值是否为promise,值'then3'不是promise:pro3.value改变为pro3.resolve参数'then3',pro3状态改变 ,执行setTimeout里的代码,由步骤8可知pro3.asyncObj={onfulfilled:pro3.onFulfilled,resolve:pro6.resolve},可是其实这里还有一步,此时进入setTimeout就是异步执行了,而在步骤13中还有一步没有执行,哪里属于主逻辑,先去执行pro5.resolve(nnvalue),由于let nnvalue = onfulfilled('then3')===pro3.resolve('then3'),pro3.resolve('then3')没有返回值,因此nnvalue=undefined, pro5.resolve(undefined)执行,判断参数undefined不是promise,修改pro5.value为undefined,修改状态,调出pro5.asyncObj,执行内部函数,由步骤7可知 pro5.then未被调用过,因此其asyncObj为空,至此步骤13结束,可是步骤14仍在继续 ,等主流程即步骤13结束之后,步骤14setTimeout内的函数开始执行-> let nnvalue=pro3.onFulfilled('then3')-> let nnValue= (value => {console.log('数据then3', value)return 'then4'})('then3'),获得nnvalue='then4',而后执行pro3.asyncObj.resolve(nnvalue)pro6.resolve('then4'),其实从13结束之后就开始了递归啦,步骤14结束。
  15. pro6.resolve('then4')触发,判断参数'then4'是否为promise,不是:修改 pro4.value='then4',修改状态,执行定时器,等待主进程结束,执行定时器内部代码,由步骤9可得 pro6.asyncObj={onfulfilled:pro6.onFulfilled,resolve:pro7.resolve} ,pro6.onfulfilled('then4')->(value=>{console.log('数据then4', value)return 'then5'})('then4'),而后获得结果nnvalue为 'then5', 执行 pro6.asyncObj.resolve('then5')pro7.resolve('then5'),步骤15结束
  16. pro7.resolve('then5')触发,判断参数'then5'是否是promise,不是:修改pro7.value='then5',修改状态,执行定时器,等待主进程结束,执行定时器内部代码,由步骤10可得 pro7.asyncObj={onfulfilled:pro7.onFulfilled,resolve:pro8.resolve} ,由于pro7.onFulfilled=undefined,执行pro7.asyncObj.resolve('then5')pro8.resolve('then5'),步骤16结束
  17. pro8.resolve('then5')触发,判断参数'then5'是否是promise,不是:修改pro8.value='then5',修改状态,执行定时器,等待主进程结束,执行定时器内部代码,由步骤11可得 pro8.asyncObj={onfulfilled:pro8.onFulfilled,resolve:pro9.resolve} ,执行pro8的onfulfilled->(value => {console.log('then5:', value) })('then5'),获得 nnvalue=undefined 执行pro8.asyncObj.resolve(undefined)pro9.resolve(undefined),步骤17结束
  18. pro9.resolve(undefined)触发,判断参数undefined是否为promise,不是:修改pro9.value=undefined,修改状态,执行定时器,由于pro9.then没有执行过,因此pro9.asyncObj没有值,因此结束。整个promise链也结束。

上面问题的答案

  1. 共有几个promise,9个
  2. 共有几个then,执行顺序:7个,1,2,4,3,6,7,8
  3. 共有几个resolve,执行顺序:10个,前3个:1,2,3, 后7个:4,3,5,6,7,8,9
相关文章
相关标签/搜索