一步步迭代实现Promise

362 阅读9分钟

第一步 基础实现

Promise 代码示例

let p = new Promise((resolve, reject) => {
    resolve('success') // reject('fail')
})
p.then((value) => {
    console.log(value)
}, (error) => {
    console.log(error)
})

代码分析

  • Promise是一个类,并在实例化的时候传入了一个立即执行的执行器
  • 执行器有两个参数,分别是resolve方法和reject方法,用来修改promise的状态,promise有三个状态,分别是 Pending,Fulfilled,Rejected
  • promise的状态确定后(成功或失败)不能更改,需要先对promise的当前状态进行判断,在进行状态更改,缓存返回值
  • then方法中,传入两个参数,即成功回调和失败回调,并对当前promise状态判断后调用

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  status = PENDING
  value = null
  reason = null
  constructor(func) {
    func(this.resolve, this.reject)
  }
  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
  }
  reject = (reason) => {
    if(this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
  }
  then(successfulCb, failedCb) {
    if (this.status === FULFILLED) {
      successfulCb(this.value)
    } else if (this.status === REJECTED) {
      failedCb(this.reason)
    }
  }
}

第二步 异步实现

代码示例

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

代码分析

  • 当执行器中包含异步方法时,then方法中不能立即拿到promise的状态,则需要对pending状态进行处理
  • then方法执行过程中,当promise状态是pending时,要将成功回调和失败回调进行缓存
  • 在异步函数结束,调用resolve或者reject的时候,再执行成功或者失败回调

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  status = PENDING
  value = null
  reason = null
  successfulCallback = null  //**改动
  failedCallback = null     //**改动
  constructor(func) {
    func(this.resolve, this.reject)
  }
  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    this.successfulCallback(this.value)   //**改动
  }
  reject = (reason) => {
    if(this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    this.failedCallback(this.reason)   //**改动
  }
  then(successfulCb, failedCb) {
    if (this.status === FULFILLED) {
      successfulCb(this.value)
    } else if (this.status === REJECTED) {
      failedCb(this.reason)
    } else {                            //**改动
      this.successfulCallback = successfulCb   //**改动
      this.failedCallback = failedCb   //**改动
    }
  }
}

实现then的链式调用

实例代码

let p = new MyPromise((resolve, reject)=> {
  setTimeout(() => {
    resolve('resolve')
  }, 2000)
  
})
p.then((value) => {
  console.log(value)
  return '链式调用1'
}, (reason) => {
  console.log(reason)
}).then((value) => {
  console.log(value)
  return '链式调用2'
}, (reason) => {
  console.log(reason)
})

代码分析

  • then 返回一个新的promise
  • 下一个then方法就是在为上一个then方法返回的promise注册回调,前面的then方法中回调的返回值,会作为后面then方法中回调的参数。
  • 如果回调中返回的是promise,那后面then方法的回调会等待他的结果
  • then的newpromise中,当状态是成功时,需要判断回调函数的结果,如果是promise则返回promise的状态,否则直接resolve,失败状态同理
  • 因为是链式调用,会传入多个成功和失败回调,所以,pending状态应该用数组来存储回调函数,生成成功和失败的回调函数队列
  • 在resolve和reject函数中,循环判断当前回调数组的长度是否大于0,对队列顶端函数调用并从队列中移除
  • 为了解决异步的情况,pending状态需要想列表中传入一个方法,并在方法中对回调函数进行调用,判断,如成功和失败的状态判断过程。

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  status = PENDING
  value = null
  reason = null
  successfulCallback = []
  failedCallback = []
  constructor(func) {
    func(this.resolve, this.reject)
  }
  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while(this.successfulCallback.length) this.successfulCallback.shift()()
  }
  reject = (reason) => {
    if(this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    while(this.failedCallback.length) this.failedCallback.shift()()
  }
  then(successfulCb, failedCb) {
    let newPromise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        let result = successfulCb(this.value)
        if (result instanceof MyPromise) {
          result.then(resolve, reject)
        } else {
          resolve(result)
        }
      } else if (this.status === REJECTED) {
        let result = failedCb(this.reason)
        if (result instanceof MyPromise) {
          result.then(resolve, reject)
        } else {
          resolve(result)
        }
      } else {
        this.successfulCallback.push(() => {
          let result = successfulCb(this.value)
          if (result instanceof MyPromise) {
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        })
        this.failedCallback.push(() => {
          let result = failedCb(this.reason)
          if (result instanceof MyPromise) {
            result.then(resolve, reject)
          } else {
            resolve(result)
          }
        })
      }
    })
    return newPromise
  }
}

异常处理1-promise的then不能返回自身的promise

示例代码

let p = new Promise((resolve, reject)=> {
  resolve('success')
})
let p1 = p.then((value) => {
  console.log(value)
  return p1
}, (error) => {
  console.log(error)
})

p1.then((value) => {
  console.log(value)
}, (error) => {
  console.log(error)   //输出error[TypeError: Chaining cycle detected for promise #<Promise>]
})

代码分析

  • 由于then返回一个promise,如果then内部的回调也返回的是同一个promise的话,相当于这个promise要等待自己完成,这是不被允许的,所以会报类型错误:Chaining cycle detected for promise
  • 在then方法获取成功或失败的结果后,需要判断这个结果是不是then所返回的promise,要获取newpromise就需要把比较过程放在定时器里面,因为定时器会开启一个宏任务,能够拿到newpromise
  • 在这里,也可以把这部分代码抽离出去,然后进行比较以及后续处理。

代码实现

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  status = PENDING
  value = null
  reason = null
  successfulCallback = []
  failedCallback = []
  constructor(func) {
    func(this.resolve, this.reject)
  }
  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while(this.successfulCallback.length) this.successfulCallback.shift()()
  }
  reject = (reason) => {
    if(this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    while(this.failedCallback.length) this.failedCallback.shift()()
  }
  then(successfulCb, failedCb) {
    let newPromise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          let result = successfulCb(this.value)
          resolvePromise(newPromise, result, resolve, reject)
        })
        
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          let result = failedCb(this.reason)
          resolvePromise(newPromise, result, resolve, reject)
        })
      } else {
        this.successfulCallback.push(() => {
          setTimeout(() => {
            let result = successfulCb(this.value)
            resolvePromise(newPromise, result, resolve, reject)
          })
        })
        this.failedCallback.push(() => {
          setTimeout(() => {
            let result = failedCb(this.reason)
            resolvePromise(newPromise, result, resolve, reject)
          })
        })
      }
    })
    return newPromise
  }
}

function resolvePromise(newPromise, result, resolve, reject) {
  if (newPromise === result) {
    return reject('TypeError: Chaining cycle detected for promise #<Promise>')
  } 
  if (result instanceof MyPromise) {
    result.then(resolve, reject)
  } else {
    resolve(result)
  }
}

异常处理2-promise执行过程中的错误处理

示例代码

let p = new Promise((resolve, reject)=> {
  resolve('success')
})

p.then((value) => {
  console.log(value)
  throw 'new error'    //在then中抛出错误
}, (error) => {
  console.log(error)
}).then(() => {}, (error) => {
  console.log(error)   //接收error
})

代码分析

  • 要捕获then中方法执行过程中抛出的错误,可以使用trycatch来对代码块进行包装,拿到错误并reject。
  • 执行器阶段也同样需要做try catch处理,去捕获执行器阶段的错误。

实现代码

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
 status = PENDING
 value = null
 reason = null
 successfulCallback = []
 failedCallback = []
 constructor(func) {
   try {                       //执行器执行错误捕获处理
     func(this.resolve, this.reject)
   } catch(e) {
     this.reject(e)
   }
 }
 resolve = (value) => {
   if (this.status !== PENDING) return
   this.status = FULFILLED
   this.value = value
   while(this.successfulCallback.length) this.successfulCallback.shift()()
 }
 reject = (reason) => {
   if(this.status !== PENDING) return
   this.status = REJECTED
   this.reason = reason
   while(this.failedCallback.length) this.failedCallback.shift()()
 }
 then(successfulCb, failedCb) {
   let newPromise = new MyPromise((resolve, reject) => {
     if (this.status === FULFILLED) {
       setTimeout(() => {
         try {                  //成功回调函数错误捕获
           let result = successfulCb(this.value)
           resolvePromise(newPromise, result, resolve, reject)
         } catch(e) {
           reject(e)
         }
         
       })
       
     } else if (this.status === REJECTED) {
       setTimeout(() => {
         try {                         //失败回调函数错误捕获
           let result = failedCb(this.reason)
           resolvePromise(newPromise, result, resolve, reject)
         } catch(e) {
           reject(e)
         }
       })
     } else {
       this.successfulCallback.push(() => {
         setTimeout(() => {
           try {                      //pending阶段缓存成功回调函数错误捕获
             let result = successfulCb(this.value)
             resolvePromise(newPromise, result, resolve, reject)
           } catch (e) {
             reject(e)
           }
         })
       })
       this.failedCallback.push(() => {
         setTimeout(() => {
           try {                      //pending阶段缓存失败回调函数错误捕获
             let result = failedCb(this.reason)
             resolvePromise(newPromise, result, resolve, reject)
           } catch (e) {
             reject(e)
           }
         })
       })
     }
   })
   return newPromise
 }
}

function resolvePromise(newPromise, result, resolve, reject) {
 if (newPromise === result) {
   return reject('TypeError: Chaining cycle detected for promise #<Promise>')
 } 
 if (result instanceof MyPromise) {
   result.then(resolve, reject)
 } else {
   resolve(result)
 }
}

promise-then函数参数处理

示例代码

let p = new Promise((resolve, reject)=> {
  resolve('success')
})

p.then(1).then(2).then((value) => {
  console.log(value)
})

代码分析

  • then中接受2个参数,分别是成功回调和失败回调
  • then方法内,需要判断回调函数是否存在,如果不存在,则将promise结果透传到下一个then函数中,以此类推。
  • 在实现的时候,需要判断回调函数是否存在,如果不存在,则将当前结果赋值给参数,让then函数生成包裹着当前结果的promise,以便后续的then方法接收

代码实现

//then方法
  then (successfulCb, failedCb) {
    successfulCb = successfulCb ? successfulCb : value => value   //判断成功回调
    failedCb = failedCb ? failedCb : reason => {throw reason}       //判断失败回调
    let newPromise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let result = successfulCb(this.value)
            resolvePromise(newPromise, result, resolve, reject)
          } catch(e) {
            reject(e)
          }
        }, 0)
        
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let result = failedCb(this.reason)
            resolvePromise(newPromise, result, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else {

        this.successfulCallback.push(() => {
          setTimeout(() => {
            try {
              let result = successfulCb(this.value)
              resolvePromise(newPromise, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.failedCallback.push(() => {
          setTimeout(() => {
            try {
              let result = failedCb(this.reason)
              resolvePromise(newPromise, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    
    return newPromise
  }

catch方法

示例代码

let p = new Promise((resolve, reject)=> {
  reject('error')
})

p.then((value) => {
  console.log(value)
  // throw 'error2'
}).catch((error) => {
  console.log(error)
})

代码分析

  • catch用来捕获promise中的错误,可以是执行器中reject的错误,也可以是then中的回调函数中抛出的错误。
  • catch的返回结果也是一个promise, 他只有一个失败的回调。
  • 所以catch的实现过程就是调用当前promise的then方法,传入失败的回调函数,并返回这个then的执行结果。

代码实现

catch(failedCb) {
    return this.then(void 0, failedCb)
}

all方法实现

代码示例

let p1 = new Promise((resolve, reject) => {
    resolve(1)
})
let p2 = new Promise((resolve, reject) => {
    resolve(2)
})
Promise.all([p1, p2]).then((result) => {
    console.log(result)
})

代码分析

  • all方法的功能是数组中所有promise都成功时的返回结果
  • all方法直接在Promise上调用,它是Promise上的一个静态方法
  • all有一个参数,是一个数组,该数组可以是promise,也可以是普通值
  • all返回一个promise,该promise的成功返回值是参数列表中promise成功结果或普通值组成的数组,失败结果会接受参数列表中第一个失败的返回值

代码实现

  static all(arr) {
    let resultArray = []
    let count = 0
    return new MyPromise((resolve, reject) => {
      function addItem(value) {
        resultArray.push(value)
        count++
        if (count === arr.length) {
          resolve(resultArray)
        }
      }
      arr.forEach((item) => {
        if (item instanceof MyPromise) {
          item.then((value) => {
            addItem(value)
          }, (reason) =>{
            reject(reason)
          })
        } else {
          addItem(item)
        }
      })
    })
  }

resolve 实现

示例代码

Promise.resolve(1).then((value) => console.log(value))

代码分析

  • resolve返回一个成功状态的promise
  • resolve在Promise类上直接调用,所以他是一个静态方法
  • resolve 接受一个参数,这个参数可能是普通值,也可能是一个promise
  • 当参数是promsie,则直接返回;如果参数是普通值,则将参数包装成promise再返回

代码实现

static resolve(value) {
    if (value instanceof MyPromise) {
      return value
    } else {
      return new MyPromise((resolve, reject) => {
        resolve(value)
      })
    }
  }

finally 实现

示例代码

let p = new Promise((resolve, reject) => {
  resolve(1)
})

p.then((value) => {
  console.log(value)
  throw 2
}).catch((error) => {
  console.log(error)
}).finally(() => {
  console.log('finally')
})

代码分析

  • finally一般then和catch的后面,不论当前promise的结果是什么,finally都被被执行
  • finally的参数是一个回调函数
  • finally后面可以继续跟then或catch 进行链式调用,所以finally方法的返回值也是一个promise
  • 实现过程是在当前promise的then的回调中分别将参数中的回调的执行结果包装成promise,然后再then中进行当前promise的值透传

代码实现

finally(callback) {
    return this.then(() => {
      return MyPromise.resolve(callback()).then((value) => value)
    }, () => {
      return MyPromise.resolve(callback()).then((reason) => {throw reason})
    })
  }

完整代码

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  status = PENDING
  value = null
  reason = null
  successfulCallback = []
  failedCallback = []
  constructor(func) {
    try {
      func(this.resolve, this.reject)
    } catch(e) {
      this.reject(e)
    }
  }
  resolve = (value) => {
    if (this.status !== PENDING) return
    this.status = FULFILLED
    this.value = value
    while(this.successfulCallback.length) this.successfulCallback.shift()()
  }
  reject = (reason) => {
    if(this.status !== PENDING) return
    this.status = REJECTED
    this.reason = reason
    while(this.failedCallback.length) this.failedCallback.shift()()
  }
  then(successfulCb, failedCb) {
    successfulCb = successfulCb ? successfulCb : value => value
    failedCb = failedCb ? failedCb : reason => { throw reason }
    let newPromise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let result = successfulCb(this.value)
            resolvePromise(newPromise, result, resolve, reject)
          } catch(e) {
            reject(e)
          }
          
        })
        
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let result = failedCb(this.reason)
            resolvePromise(newPromise, result, resolve, reject)
          } catch(e) {
            reject(e)
          }
        })
      } else {
        this.successfulCallback.push(() => {
          setTimeout(() => {
            try {
              let result = successfulCb(this.value)
              resolvePromise(newPromise, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.failedCallback.push(() => {
          setTimeout(() => {
            try {
              let result = failedCb(this.reason)
              resolvePromise(newPromise, result, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return newPromise
  }
  catch (failedCb) {
    return this.then(void 0, failedCb)
  }
  finally(callback) {
    return this.then(() => {
      return MyPromise.resolve(callback()).then((value) => value)
    }, () => {
      return MyPromise.resolve(callback()).then((reason) => {throw reason})
    })
  }
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value
    } else {
      return new MyPromise((resolve, reject) => {
        resolve(value)
      })
    }
  }
  static all(arr) {
    let resultArray = []
    let count = 0
    return new MyPromise((resolve, reject) => {
      function addItem(value) {
        resultArray.push(value)
        count++
        if (count === arr.length) {
          resolve(resultArray)
        }
      }
      arr.forEach((item) => {
        if (item instanceof MyPromise) {
          item.then((value) => {
            addItem(value)
          }, (reason) =>{
            reject(reason)
          })
        } else {
          addItem(item)
        }
      })
    })
    
  }
}

function resolvePromise(newPromise, result, resolve, reject) {
  if (newPromise === result) {
    return reject('TypeError: Chaining cycle detected for promise #<Promise>')
  } 
  if (result instanceof MyPromise) {
    result.then(resolve, reject)
  } else {
    resolve(result)
  }
}

————————————————个人杂记————————————————————