美文网首页
实现一个Promise

实现一个Promise

作者: MDIF | 来源:发表于2021-01-10 16:26 被阅读0次

    实现一个Promise,来学习一下Promise的源码思想。
    Promise有三种状态,分别是pending,fulfilled,rejected。默认是pending状态,当调用resolve或reject时改变状态。
    状态可以从pending变成resolve或者reject,一旦改变就无法在改变回去了 。
    基本用法

    let p = new MyPromise((resolve,reject)=>{
      resolve('成功')
    })
    p.then(value=>console.log(value),err=>console.log(err))
    

    首先需要声明一个构造函数,构造函数接受一个函数作为参数,该参数会立即执行。

    function MyPromise(executor){
      executor()
    }
    

    executor接受两个参数resolve,reject

    function MyPromise(executor){
      function resolve(){}
      function reject(){}
      try{
        executor(resolve,reject)
      }catch(e){
        reject(e)
     }
    }
    

    我们还需要一个变量来记录Promise的状态,调用resolve,reject时改变promise状态。
    因为Promise的状态只能在pending状态改变,状态在resolve或reject状态都是不能改变的,所以需要判断。

    function MyPromise(executor){
      let self =  this
      self.status = 'pending'
      function resolve(){
        if(self.status === 'pending'){
          self.status = 'resolve'
        }
      }
      function reject(){
         if(self.status === 'pending'){
          self.status = 'reject'
        }
       }
       try{
        executor(resolve,reject)
       }catch(e){
         reject(e)
       }
    }
    

    在调用resolve或reject时会传递一个值,并录下来。以便在then的回调中使用。

    function MyPromise(executor){
      let self =  this
      self.status = 'pending'
      self.value
      self.reason
      function resolve(value){
        if(self.status === 'pending'){
          self.status = 'resolve'
          self.value = value
        }
      }
      function reject(reason){
         if(self.status === 'pending'){
          self.status = 'reject'
          self.reason= reason
        }
     }
     try{
        executor(resolve,reject)
     }catch(e){
       reject(e)
     }
    }
    

    Promise有一个then方法,then接受两个函数作为参数,分别是onFulfilled,onFailed。
    第一个是onFulfilled,成功回调。
    第二个是onFailed,失败回调。
    onFulfilled,onFailed被调用时会将resolve或reject执行时保存的变量传递过去。

    MyPromise.prototype.then = function(onFulfilled,onFailed){
      let self = this
      if(self.status ==='resolve')  {
        onFulfilled(self.value)
      }
      if(self.status ==='reject')  {
        onFailed(self.reason)
      }
    }
    

    但是上面的实现是不支持异步的

    let p = new MyPromise((resolve,reject)=>{
      setTimeout(()=>{
        resolve('成功')  
      },2000)
    })
    p.then(value=>console.log(value),err=>console.log(err))
    

    上面的代码想着会打印出来成功,但实际上是不会打印出任何东西的。
    因为在执行到then的时候还没有执行resolve,这意味着Promise的状态还没有改变,没改变的话,执行then函数,是什么都不会做的。等到2秒后resolve执行了,状态改了,但是then已经执行过了一次了。
    所以需要继续完善代码。

    function MyPromise(executor){
      let self =  this
      self.status = 'pending'
      self.value
      self.reason
      //声明两个数组来记住then传过来的函数
      self.onResolvedCallBacks = []
      self.onRejectedCallBacks = []
      function resolve(value){
        if(self.status === 'pending'){
          self.status = 'resolve'
          self.value = value
          self.onResolvedCallBacks.forEach(function(fn){
              fn()
          })
        }
      }
      function reject(reason){
         if(self.status === 'pending'){
          self.status = 'reject'
          self.reason= reason
          self.onRejectedCallBacks.forEach(function(fn){
              fn()
          })
        }
     }
     try{
       executor(resolve,reject)
     }catch(e){
       reject(e)
     }
    }
    
    MyPromise.prototype.then = function(onFulfilled,onFailed){
      let self = this
      if(self.status ==='resolve')  {
        onFulfilled(self.value)
      }
      if(self.status ==='reject')  {
        onFailed(self.reason)
      }
      //如果执行到then,状态为pending,把传来的两个函数保存起来,等到状态改变在调用
      if(self.status ==='pending'){
        self.onResolvedCallBacks.push(function(){
          onFulfilled(self.value)
        })
        self.onRejectedCallBacks.push(function(){
          onFailed(self.reason)
        })
      }
    }
    

    再次测试,会在1秒之后打印出成功1,2秒后打印出成功2

    let p1 = new MyPromise((resolve,reject)=>{
      setTimeout(()=>{
        resolve('成功1')  
      },1000)
    })
    let p2 = new MyPromise((resolve,reject)=>{
      setTimeout(()=>{
        resolve('成功2')  
      },2000)
    })
    p1.then(value=>console.log(value),err=>console.log(err))
    p2.then(value=>console.log(value),err=>console.log(err))
    
    链式调用

    Promise是支持链式调用的,调用then后会返回一个新的Promise。

    MyPromise.prototype.then = function(onFulfilled,onFailed){
      let self = this
      let promise2 = new MyPromise(function(resolve,reject){
        if(self.status ==='resolve')  {
          onFulfilled(self.value)
        }
        if(self.status ==='reject')  {
          onFailed(self.reason)
        }
        //如果执行到then,状态为pending,把传来的两个函数保存起来,等到状态改变在调用
        if(self.status ==='pending'){
          self.onResolvedCallBacks.push(function(){
            onFulfilled(self.value)
          })
          self.onRejectedCallBacks.push(function(){
            onFailed(self.reason)
          })
        }
      })
      return promise2
    }
    

    接下来我们需要拿到then的onFulfilled和onFailed返回的值
    如果返回的是普通值,直接调用promise2的resolve,该值作为resolve的参数。这样下一个then就可以获取到该值
    如果是Promise则调用这个Promise.
    下面声明一个新函数resolvePromise来进行判断

    function resolvePromise(promise2,x,resolve,reject){
      if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
        //如果then为function类型,则认为返回了一个Promise
        try{
          let then = x.then
          if(typeof then ==='function'){
          //返回promise处理逻辑
          }else{
            //普通对象
             resolve(x) 
          }
        }catch(e){
          reject(e)
        }
      }else{
        //普通值
        resolve(x)
      }
    }
    

    如果promise2 和 x 引用了同一个对象,需要抛出一个错误

    function resolvePromise(promise2,x,resolve,reject){
      //避免返回自己
      if(promise2 === x){throw new Error('循环引用了)}
    
      if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
        //如果返回值then为function类型,则认为返回了一个Promise
        try{
          let then = x.then
          if(typeof then ==='function'){
          //返回promise处理逻辑
          }else{
            //普通对象
             resolve(x) 
          }
        }catch(e){
          reject(e)
        }
      }else{
        //普通值
        resolve(x)
      }
    }
    

    如果返回的是Promise的话,参照Promise/A+规范编写逻辑

        //2.3.3.1 let then = x.then.
        //2.3.3.2 如果 x.then 这步出错,那么 reject promise with e as the reason..
        //2.3.3.3 如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
        //    2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
        //    2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r.
        //    2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
        //    2.3.3.3.4 如果调用then抛出异常e 
        //        2.3.3.3.4.1 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
        //       2.3.3.3.4.3 否则,reject promise with e as the reason
    

    上面规范翻译取自https://github.com/YvetteLau/Blog/issues/2

    function resolvePromise(promise2,x,resolve,reject){
      //避免返回自己
      if(promise2 === x){throw new Error('循环引用了)}
      if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
        //如果返回值then为function类型,则认为返回了一个Promise
       try{
          let then = x.then
          if(typeof then ==='function'){
          //返回promise处理逻辑
            then.call(x,function(y){
              //这里不能直接resolve(y),因为有可能会有嵌套promise的情况,需要用递归来处理
              resolvePromise(promise2,y,resolve,reject)
            },function(r){
              reject(r)  
            })
          }else{
            //普通对象
             resolve(x) 
          }
        }catch(e){
          reject(e)
        }  
      }else{
        //普通值
        resolve(x)
      }
    }
    

    根据规范我们需要确保如果resolve和reject都调用了,优先调用第一个,剩下的忽略。所以需要用一个标志来判断

    function resolvePromise(promise2,x,resolve,reject){
      //避免返回自己
      if(promise2 === x){throw new Error('循环引用了')}
      if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
        let called
        //如果返回值then为function类型,则认为返回了一个Promise
        try{
          let then = x.then
          if(typeof then ==='function'){
          //返回promise处理逻辑
            then.call(x,function(y){
              //这里不能直接resolve(y),因为有可能会有嵌套promise的情况,需要用递归来处理'
              if(called){return}
              called = true
              resolvePromise(promise2,y,resolve,reject)
            },function(r){
              if(called){return}
              called = true
              reject(r)  
            })
          }else{
            //普通对象
            if(called){return}
            called = true
            resolve(x) 
          }
        }catch(e){
          if(called){return}
          called = true
          reject(e)    
        }
      }else{
        //普通值
        resolve(x)
      }
    }
    

    假如没有传递then的参数

    p.then().then().then(res=>{console.log(res)})
    

    根据规定
    2.2.7.3 如果 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled
    2.2.7.4 如果 onRejected 不是一个函数,promise2 以promise1的reason rejected

    MyPromise.prototype.then = function(onFulfilled,onFailed){
      //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
      onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : function(value)  {return value}
      onFailed = typeof onFailed==='function' ? onFailed: function(reason)  {throw reason}
    
      let self = this
      let promise2 = new MyPromise(function(resolve,reject){
        if(self.status ==='resolve')  {
          onFulfilled(self.value)
        }
        if(self.status ==='reject')  {
          onFailed(self.reason)
        }
        //如果执行到then,状态为pending,把传来的两个函数保存起来,等到状态改变在调用
        if(self.status ==='pending'){
          self.onResolvedCallBacks.push(function(){
            onFulfilled(self.value)
          })
          self.onRejectedCallBacks.push(function(){
            onFailed(self.reason)
          })
        }
      })
      return promise2
    }
    

    接下来我们在then中调用写好的resolvePromise方法

    MyPromise.prototype.then = function(onFulfilled,onFailed){
      //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
      onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : function(value)  {return value}
      onFailed = typeof onFailed==='function' ? onFailed: function(reason)  {throw reason}
    
      let self = this
      var promise2 = new MyPromise(function(resolve,reject){
        if(self.status ==='resolve'){
          //规范规定onFulfilled和onFailed需要是微任务,这里使用宏任务来模拟,原生Promise的实现并不是这样的。
          setTimeout(function(){
            try{
            const x = onFulfilled(self.value)
              resolvePromise(promise2,x,resolve,reject)
            }catch(e){
              reject(e)
            }
          },0)
        }
        if(self.status ==='reject'){
          setTimeout(function(){
            try{
              const x = onFailed(self.reason)
              resolvePromise(promise2,x,resolve,reject)
            }catch(e){
              reject(e)
            }
          },0)
        }
        if(self.status ==='pending'){
          self.onResolvedCallBacks.push(function(){
            setTimeout(function(){
              try{
                const x = onFulfilled(self.value)  
                resolvePromise(promise2,x,resolve,reject)
              }catch(e){
                reject(e)
              }
            },0)
          })
          self.onRejectedCallBacks.push(function(){
            setTimeout(function(){
              try{
                const x = onFailed(self.reason)  
                resolvePromise(promise2,x,resolve,reject)
              }catch(e){
                reject(e)
              }
            },0)
          })
        }
      })
      return promise2
    }
    

    至此一个Promise已经实现了。全部代码如下

    function MyPromise(executor){
      let self =  this
      self.status = 'pending'
      self.value
      self.reason
      //声明两个数组来记住then传过来的函数
      self.onResolvedCallBacks = []
      self.onRejectedCallBacks = []
      function resolve(value){
        if(self.status === 'pending'){
          self.status = 'resolve'
          self.value = value
          self.onResolvedCallBacks.forEach(function(fn){
              fn()
          })
        }
      }
      function reject(reason){
         if(self.status === 'pending'){
          self.status = 'reject'
          self.reason= reason
          self.onRejectedCallBacks.forEach(function(fn){
              fn()
          })
        }
     }
     try{
       executor(resolve,reject)
     }catch(e){
       reject(e)
     }
    }
    
    function resolvePromise(promise2,x,resolve,reject){
      //避免返回自己
      if(promise2 === x){throw new Error('循环引用了')}
      if((typeof x !==null && typeof x ==='object') || typeof x ==='function'){
        let called
        //如果返回值then为function类型,则认为返回了一个Promise
        try{
          let then = x.then
          if(typeof then ==='function'){
          //返回promise处理逻辑
            then.call(x,function(y){
              //这里不能直接resolve(y),因为有可能会有嵌套promise的情况,需要用递归来处理'
              if(called){return}
              called = true
              resolvePromise(promise2,y,resolve,reject)
            },function(r){
              if(called){return}
              called = true
              reject(r)  
            })
          }else{
            //普通对象
            if(called){return}
            called = true
            resolve(x) 
          }
        }catch(e){
          if(called){return}
          called = true
          reject(e)    
        }
      }else{
        //普通值
        resolve(x)
      }
    }
    
    MyPromise.prototype.then = function(onFulfilled,onFailed){
      //PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
      onFulfilled = typeof onFulfilled ==='function' ? onFulfilled : function(value)  {return value}
      onFailed = typeof onFailed==='function' ? onFailed: function(reason)  {throw reason}
    
      let self = this
      var promise2 = new MyPromise(function(resolve,reject){
        if(self.status ==='resolve'){
          //规范规定onFulfilled和onFailed需要是微任务,这里使用宏任务来模拟,原生Promise的实现并不是这样的。
          setTimeout(function(){
            try{
            const x = onFulfilled(self.value)
              resolvePromise(promise2,x,resolve,reject)
            }catch(e){
              reject(e)
            }
          },0)
        }
        if(self.status ==='reject'){
          setTimeout(function(){
            try{
              const x = onFailed(self.reason)
              resolvePromise(promise2,x,resolve,reject)
            }catch(e){
              reject(e)
            }
          },0)
        }
        if(self.status ==='pending'){
          self.onResolvedCallBacks.push(function(){
            setTimeout(function(){
              try{
                const x = onFulfilled(self.value)  
                resolvePromise(promise2,x,resolve,reject)
              }catch(e){
                reject(e)
              }
            },0)
          })
          self.onRejectedCallBacks.push(function(){
            setTimeout(function(){
              try{
                const x = onFailed(self.reason)  
                resolvePromise(promise2,x,resolve,reject)
              }catch(e){
                reject(e)
              }
            },0)
          })
        }
      })
      return promise2
    }
    
    Promise.prototype.catch

    相当于调用then,但是只传递reject。

    MyPromise.prototype.catch = function(onRejected){
        return this.then(function(null,onRejected))
    }
    
    Promise.resolve

    Promise.resolve(value)
    返回一个以给定值解析后的Promise对象。
    如果value是promise则返回这个promise
    如果是thenable对象,返回的promise采用这个thenable的最终状态
    如果是其他情况则返回成功状态。

    MyPromise.resolve = function(value){
      if(value instanceof Promise){return value}
      return new MyPromise(function(resolve,reject){
        if(value && value.then && typeof value.then === 'function'){
          value.then(resolve,reject)
        }else{
          resolve(value)
        }
      })
    }
    
    Promise.reject

    Promise.reject(reason)
    返回一个reject状态的promise。

    MyPromise.reject = function(reason){
      return new MyPromise(function(resolve,reject){
          reject(reason)
        })
    }
    
    promise.all

    一次传入多个异步任务,全部成功后返回一个数组,数组结果的顺序和传入的相同。如果有一个失败则失败。

    MyPromise.all = function(promises){
      if(!Array.isArray(promises)){throw TypeError('type error of params')}
      return new MyPromise((resolve,reject)=>{
        let resultArr = []
        promises.forEach((p,index)=>{
          MyPromise.resolve(p).then(res=>{
            resultArr[index] = res
            if(resultArr.length === promises.length){
            console.log('here'+resultArr+promises.length)
              resolve(resultArr)
            }
          },err=>reject(err))
        })
      })
    }
    
    promise.race
    MyPromise.race = function(promises){
        return new MyPromise((resolve,reject)=>{
            promises.forEach((p,index)=>{
                //有可能不是promise所以先用resolve解析一下
                MyPromise.resolve(p).then(resolve,reject)
            })
        })
    }
    

    相关文章

      网友评论

          本文标题:实现一个Promise

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