实现Promise

107 阅读6分钟

Promise是实现异步表现的一种方式,存在pending,fulfilled,rejected三种状态,微任务的一种

1.基本结构

  • 初始化状态
  • 创建reslove和reject方法
  • 创建基本的then方法
  • 实例化的时候立即执行reslove/reject相关的操作
const PENDING = "PENDING" //待定状态,也就是还有完成的状态
const FULFILLED = "FULFILLED" //操作成功状态
const REJECTED = "REJECTED" //操作失败状态

class MyPromise(){
    constructor(excutor){ 
        //promise会传入一个立即执行的函数,传入的函数其中包含resolve和reject连个函数参数
        //把内部的reslove和reject分别传入代替的参数函数,其实就是一个回调
        //用箭头函数,是因为在外部调用的,为了内部this成功绑定实例,否则可能this绑定失败
        try{
            excutor(this.reslove, this.reject) 
        }catch(err){
            this.reject(err)
        }
    }
    
    status = PENDING //当前的状态
    value = null //成功的值
    reason = null //失败的值
    
    reslove = (value) => {
        if(this.status === PENDING){ //执行完成函数的时候,如果状态是等待则设置为成功
            this.status === FULFILLED
            this.value = value //设置成功的值
            
        }
    } //成功的执行函数
    
    reject = (reason) => {
        if(this.status === PENDING){ //执行失败函数的时候,如果状态是等待则设置为失败
            this.status === REJECTED
            this.reason = reason //设置失败的值
            
        }
    } //失败的执行函数
    
    then(onResolve, onReject){
        if(this.status === FULFILLED){
            onResolve(this.value)
        } else if (this.status === REJECTED){
            onReject(this.reason)
        } else if (this.status === PENDING){
            ...
        }
    }
}

2.实现异步操作

  • 基本结构只能实现同步,如果异步则需要对成功和失败分别创建一个队列,然后等待时机再执行
  • 在then函数内部把对应的操作推入对应的队列
class MyPromise(){
    ...
    onFulfilledQueue = [] //成功操作的队列
    onRejectedQueue = [] //失败操作的队列
    
    reslove = (value) => {
        if(this.status === PENDING){ //执行完成函数的时候,如果状态是等待则设置为成功
            ...
            
            while(this.onFulfilledQueue.length > 0){
                //如果队列还有操作函数,则依次推出执行,知道队列为空
                const itemFn = this.onFulfilledQueue.shift()
                if(typeof itemFn === "function") itemFn(this.value)
            }
        }
    } //成功的执行函数
    
    reject = (reason) => {
        if(this.status === PENDING){ //执行失败函数的时候,如果状态是等待则设置为失败
            ...
             while(this.onRejectedQueue.length > 0){
                //如果队列还有操作函数,则依次推出执行,知道队列为空
                const itemFn = this.onRejectedQueue.shift()
                if(typeof itemFn === "function") itemFn(this.value)
            }
        }
    } //失败的执行函数
    
    then(onResolve, onReject){
        if(this.status === FULFILLED){
            onResolve(this.value)
        } else if (this.status === REJECTED){
            onReject(this.reason)
        } else if (this.status === PENDING){
            this.onFulfilledQueue.push(onResolve)
            this.onRejectedQueue.push(onReject)
        }
    }
}

3.实现链式调用

  • 由于要实现链式调用所以then函数内部需要返回一个同样的promise
  • 需要等到实例完成才能在内部使用当前实例,所以需要在当前同步的添加一个微任务操作
  • 简单的可以用setTimout,但是这样相当于开了个宏任务,为了更好操作推荐queueMicrotask来开一个微任务
  • 错误处理,比如then处理的值不能是当前的promise否则就会死循环
function reslovePromise = (p, x, resolve, reject){
    //创建一个helper方便操作每一步的then的resolve值
    //then参数依旧是一个回调,用于处理每一步的值
    //x为当前then计算后的值
    //p为当前的promise实例
    
    if(p === x){ 
        //如果计算出来的值就是当前的promise则会报循环错误
        retrun reject(new Error())
    }
    if(x instanceof MyPromise){
        //如果值是一个promise则直接调用then
        x.then(resolve, reject)
    }else{
        //否则直接执行完成函数
        resolve(x) 
    }
}

class MyPromise(){
    ...
    then(onResolve, onReject){
        //由于then参数可以不传所以需要兼容,成功则返回值,失败抛出错误
        onResolve = typeof onResolve === "function" ? onResolve : value => value
        onReject = typeof onReject === "function" ? onReject : reason => throw reason
        
        const p = new MyPromise((resolve, reject) => { 
            //创建一个新的实例,然后返回改实例实现链式调用
            
            cosnt onFulfilledTaskHandle = () => { //完成时候的操作
                queueMicrotask(() => { 
                    //由于要用到p,所以添加一个微任务即可获取到
                    //否则就会取不到报错,因为p还没有创建好
                    try{
                        //因为计算可以是任何类型,也可能报错,所以需要错误处理
                        const x = onResolve(this.value) //计算新的值
                        reslovePromise(p, x, resolve, reject)
                    }catch(err){
                        reject(err)
                    }
                })
            }
            
            cosnt onRejectedTaskHandle = () => { //错误时候的操作
                queueMicrotask(() => { 
                    //由于要用到p,所以添加一个微任务即可获取到
                    //否则就会取不到报错,因为p还没有创建好
                    try{
                        //因为计算可以是任何类型,也可能报错,所以需要错误处理
                        const x = onReject(this.reson) //计算新的错误值
                        reslovePromise(p, x, resolve, reject)
                    }catch(err){
                        reject(err)
                    }
                })
            }
            
            if(this.status === FULFILLED){
                onFulfilledTaskHandle()
            } else if (this.status === REJECTED){
                onRejectedTaskHandle()
            } else if (this.status === PENDING){
                this.onFulfilledQueue.push(onFulfilledTaskHandle)
                this.onRejectedQueue.push(onRejectedTaskHandle)
            }
        })
        
        return p
    }
}

4.添加resolve/reject静态方法

  • static或直接MyPromise.[key]
class MyPromise(){
    ...
    static resolve(value){
        if(value instanceof MyPromise) return value
        
        return new MyPromise(resolve => {resolve(value)}) //直接成功于传入的值
    }
    
    static reject(reason){
        return new MyPromise((resolve, reject) => {reject(reason)}) //直接失败原因
    }
}

5.添加catch和finally

  • catch利用then直接reject
  • finally利用then直接返回对应的,因为不管成功还是失败都会执行
class MyPromise(){
    ...
    catch(onRejected){
        return this.then(null, onRejected) //不传入成功参数
    }
    
    finally(callback){
        return this.then(
            data => { //如果成功则走此,直接返回一个成功的
                return MyPromise.resolve(callback()).then(() => data)
            },
            error => { //如果失败则走此,直接返回一个失败的抛出一个错误error
                return MyPromise.reject(callback()).then(() => {throw error}) 
            }
        )
    }
}

6.添加all,race,allSattled静态方法

  • all-返回全部成功的,如果有一个失败的都不返回
  • allSattled-返回所有的结果包括成功和失败
  • race最快的返回
class MyPromise(){
    ...
    static all(promiseArr){
        return new MyPromise((resolve, reject) => {
            let res = []
            let counts = promiseArr.length
            
            promiseArr.forEatch((p, idx) => {
                p.then(data => {
                  res[idx] = data
                  counts -= 1
                  if(counts === 0) resolve(res)
                }, reject())
            })
        })
    }
    
    static allSattled(promiseArr){
        return new MyPromise((resolve, reject) => {
            let res = []
            let counts = promiseArr.length
            
            promiseArr.forEatch((p, idx) => {
                p.then(
                    data => {
                      res[idx] = { status: true, data }
                      counts -= 1
                      if(counts === 0) resolve(res)
                    }, 
                    err => {
                      res[idx] = { status: false, err }
                      counts -= 1
                      if(counts === 0) reject(res)
                    }
                )
            })
        })
    }
    
    static race(promiseArr){
        return new MyPromise((resolve, reject) => {
            for(let p of promiseArr){
                p.then(resolve, reject)
            }
        })
    }
}

7.最终实现

const PENDING = "PENDING"
const FULFILLED = "FULFILLED"
const REJECTED = "REJECTED"

function resolvePromise(p, x, res, rej){
  if(p === x){
    return rej(new TypeError("promise cycle error"))
  }
  if(x instanceof MyPromise){
    x.then(res, rej)
  }else {
    res(x)
  }
}

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    }catch (error){
      this.reject(error)
    }
  }

  status = PENDING
  value = null
  reason = null
  onFulfilledCallBackQueue = []
  onRejectedCallBackQueue = []

  resolve = (value) => {
    if(this.status === PENDING){
      this.status = FULFILLED
      this.value = value

      while (this.onFulfilledCallBackQueue.length){
        const itemFun = this.onFulfilledCallBackQueue.shift()
        if(typeof itemFun === "function"){
          itemFun(value)
        }
      }
    }
  }

  reject = (reason) => {
    if(this.status === PENDING){
      this.status = REJECTED
      this.reason = reason

      while (this.onRejectedCallBackQueue.length){
        const itemFun = this.onRejectedCallBackQueue.shift()
        if(typeof itemFun === "function"){
          itemFun(reason)
        }
      }
    }
  }

  then(onFulfilled, onRejected){

    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
    onRejected = typeof onRejected === "function" ? onRejected : reason => {throw reason}

    const p = new MyPromise((resolve, reject) => {

      const fulfilledTask = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(p, x, resolve, reject)
          }catch (error){
            reject(error)
          }
        })
      }

      const rejectedTask = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(p, x, resolve, reject)
          }catch (error){
            reject(error)
          }
        })
      }

      if(this.status === FULFILLED){
        fulfilledTask()
      } else if(this.status === REJECTED){
        rejectedTask()
      } else if(this.status === PENDING){
        this.onFulfilledCallBackQueue.push(fulfilledTask)
        this.onRejectedCallBackQueue.push(rejectedTask)
      }
    })

    return p
  }

  catch(onRejected){
    return this.then(null, onRejected)
  }

  finally(callback){
    return this.then(data => {
      return MyPromise.resolve(callback()).then(() => data)
    }, error => {
      return MyPromise.reject(callback()).then(() => {throw error})
    })
  }

  static resolve(value){
    if(value instanceof MyPromise){
      return value
    }

    return new MyPromise(resolve => {
      resolve(value)
    })
  }

  static reject(reason){
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promiseArr){
    return new MyPromise(function (resolve, reject){
      let res = []
      let counts = promiseArr.length
      promiseArr.forEach((p, idx) => {
        p.then(data => {
          res[idx] = data
          counts -= 0
          if(counts === 0) resolve(res)
        }, reject())
      })
    })
  }

  static allSettled(promiseArr){
    return new MyPromise(function (resolve, reject){
      let res = []
      let counts = promiseArr.length

      promiseArr.forEach((p, idx) => {
        p.then(value => {
          res[idx] = {
            status: FULFILLED,
            value
          }
          counts -= 1
          if(counts === 0) resolve(res)
        }, reason => {
          res[idx] = {
            status: REJECTED,
            reason
          }
          counts -= 1
          if(counts === 0) resolve(res)
        })
      })
    })
  }

  static race(promiseArr){
    return new MyPromise(function (resolve, reject){
      for(let p of promiseArr){
        p.then(resolve, reject)
      }
    })
  }
}
```