实现一个Promise

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

推荐阅读更多精彩内容