Promise源码,根据PromiseA+规范实现一个相对完美的Promise

先看一下PromiseA+规范:

  • 一个Promise必须处在其中之一的状态:pending, fulfilled 或 rejectedpending可以向后两种状态转移,而后两种状态是稳定的,进入之后就永远不会变更且附带一个状态值
  • 提供then方法来以访问其当前的值、终值和拒因
  • then方法接受两个参数,onFulfilled是当Promise状态变为Fullfilled的状态的回调,onRejected是当Promise状态变为Rejected的回调,两个参数都必须是函数,并且满足1.promise之前结束前不可调用 。2.调用次数不超过一次。3.值不是函数忽略。
  • then返回一个promise方法,下一次then受上一次then返回结果的影响

总结一下我们要实现的:

  1. 维护promise实例内部状态,PENDING,FULLFILLED,REJECTED,实现resolve,reject方法来进行状态的单向流转

  2. 实现一个then方法:

    1. 接受两个函数作为异步回调

    2. then方法可以进行链式的调用,以上一次的返回值来决定下一次Promise的状态

    3. 对于接收函数的返回值要进行类型判断的处理

  3. 一些优化

    1. 对于resolve,reject接收的值夜要进行类型的判断进行不同的处理

    2. all,race等一些静态方法的实现

下面我们开始一步一步的实现我们的Promise


一、维护promise实例内部状态,PENDING,FULLFILLED,REJECTED,实现resolve,reject方法来进行状态的单向流转

// 定义3个状态常量,如果用TS的话可以用枚举
const PENDING = 'PENDING'
const FULLFILLED = 'FULLFILLED'
const REJECTED = 'REJECTED'

// 要实现的promise类
class MyPromise {
  status
  value
  reason

  constructor(exec) {
    // 初始状态为PENDING
    this.status = PENDING
    exec(this.resolve.bind(this), this.reject.bind(this))
  }

  resolve(value) {
    // 状态仅可以变更一次
    if (this.status === PENDING) {
      this.value = value
      this.status = FULLFILLED
    }
  }

  reject(reason) {
    // 状态仅可以变更一次
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
    }
  }
}


二、then方法的实现

这里分几步来依次完善then方法

  • 实现异步回调和链式调用

        链式调用只要返回一个新的promise即可

  then(onFullfilled, onRejected) {
    // 返回 promise
    let newP = new MyPromise((resolve, reject) => {
      // 将传入的两个函数封装成微任务
      const fullfilled = val => {
        queueMicrotask(() => {
          let res = onFullfilled(val)
          resolve(res)
        })
      }
      const rejceted = reason => {
        queueMicrotask(() => {
          let res = onRejected(reason)
          reject(res)
        })
      }
      // 如果状态已经是FULLFILLED,立刻执行fullfilled方法
      if (this.status === FULLFILLED) {
        fullfilled(this.value)
      }
      // 如果状态已经是REJECTED,立刻执行rejceted方法
      if (this.status === REJECTED) {
        rejceted(this.reason)
      }
      // 如果状态是PENDING,将两个任务推入待执行队列
      if (this.status === PENDING) {
        this.successFnList.push(fullfilled)
        this.errorFnList.push(rejceted)
      }
    })
    return newP
  }
  • 对传入函数的执行的返回值进行处理

    我们知道还有这样一个规则,传入的两个回调函数的返回值如果是一个promise,那么.then方法所创建的新的promise的状态将会有这个return的promise来决定,所以我们需要一个函数来处理不同类型的返回值

先写出这个函数

/**
 * @param {*} promise 执行回调函数的那个promise
 * @param {*} x 回调函数执行返回值
 * @param {*} resolve promise中的resolve
 * @param {*} reject promise中的reject
 * @param {*} end 这个值在fullfilled中是resolve,在rejected中是reject,在如果x的值是普通类型,将会由end来处理
 */
function handlePromiseEnd(promise, x, resolve, reject, end) {
  // 避免套娃
  if (promise === x) {
    return reject(new Error('error'))
  }

  if (typeof x === 'function') {
    // 如果是方法,执行完,对结果再判断一次
    let rx = x()
    handlePromiseEnd(promise, rx, resolve, reject, end)
  } else if (x instanceof MyPromise) {
    // 如果是promise,end走那条路就交给promise来决定了
    let onResolved = val => {
      handlePromiseEnd(x, val, resolve, reject, resolve)
    }
    let onRejected = reason => {
      handlePromiseEnd(x, reason, resolve, reject, reject)
    }

    // 别问我为什么非要套个微任务,源码就是这样写的,我能怎么办呢
    queueMicrotask(() => {
      x.then(onResolved, onRejected)
    })
  } else {
    // 直到结果是一个普通值,调用end处理
    end(x)
  }
}

最终的then函数就是这样

  then(onFullfilled, onRejected) {
    // 返回 promise
    let newP = new MyPromise((resolve, reject) => {
      // 将传入的两个函数封装成微任务
      const fullfilled = val => {
        queueMicrotask(() => {
          let res = onFullfilled(val)
          handlePromiseEnd(newP, res, resolve, reject, resolve)
        })
      }
      const rejceted = reason => {
        queueMicrotask(() => {
          let res = onRejected(reason)
          handlePromiseEnd(newP, res, resolve, reject, reject)
        })
      }
      // 如果状态已经是FULLFILLED,立刻执行fullfilled方法
      if (this.status === FULLFILLED) {
        fullfilled(this.value)
      }
      // 如果状态已经是REJECTED,立刻执行rejceted方法
      if (this.status === REJECTED) {
        rejceted(this.reason)
      }
      // 如果状态是PENDING,将两个任务推入待执行队列
      if (this.status === PENDING) {
        this.successFnList.push(fullfilled)
        this.errorFnList.push(rejceted)
      }
    })

    return newP
  }


三、进一步完善

promise主要部分已经完成,还需要完善两个方面

1. 对resolve,rejcet接收的值进行不同类型的处理

2. 添加all,race,resolve,reject4个静态方法

这里直接贴上全部代码

// 1. 状态的单向流转
// 2. then 异步回调,链式调用,返回值处理
// 3. resolve,reject在接收promise时的状态移交
// 4. 静态方法:resolve,reject,race,all

const PENDING = 'PENDING'
const FULLFILLED = 'FULLFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
  status
  value
  reason
  successFnList
  errorFnList

  constructor(exec) {
    this.status = PENDING
    this.successFnList = []
    this.errorFnList = []
    exec(this.resolve.bind(this), this.reject.bind(this))
  }

  resolve(value) {
    if (this.status === PENDING) {
      if (value instanceof MyPromise) {
        value.then(this.resolve.bind(this), this.reject.bind(this))
      } else {
        this.value = value
        this.status = FULLFILLED
        this.successFnList.forEach(fn => {
          fn(this.value)
        })
      }
    }
  }

  reject(reason) {
    if (this.status === PENDING) {
      if (reason instanceof MyPromise) {
        value.then(this.resolve.bind(this), this.reject.bind(this))
      } else {
        this.reason = reason
        this.status = REJECTED
        this.errorFnList.forEach(fn => {
          fn(this.reason)
        })
      }
    }
  }

  then(onFullfilled, onRejected) {
    // 返回 promise
    let newP = new MyPromise((resolve, reject) => {
      // 将传入的两个函数封装成微任务
      const fullfilled = val => {
        queueMicrotask(() => {
          let res = onFullfilled(val)
          handlePromiseEnd(newP, res, resolve, reject, resolve)
        })
      }
      const rejceted = reason => {
        queueMicrotask(() => {
          let res = onRejected(reason)
          handlePromiseEnd(newP, res, resolve, reject, reject)
        })
      }
      // 如果状态已经是FULLFILLED,立刻执行fullfilled方法
      if (this.status === FULLFILLED) {
        fullfilled(this.value)
      }
      // 如果状态已经是REJECTED,立刻执行rejceted方法
      if (this.status === REJECTED) {
        rejceted(this.reason)
      }
      // 如果状态是PENDING,将两个任务推入待执行队列
      if (this.status === PENDING) {
        this.successFnList.push(fullfilled)
        this.errorFnList.push(rejceted)
      }
    })

    return newP
  }

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

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

  static reject(reason) {
    if (reason instanceof MyPromise) {
      return reason
    }

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

  static race(promiseList) {
    if (promiseList.length === 0) {
      return
    }
    return new MyPromise((resolve, rejcet) => {
      promiseList.forEach(promise => {
        promise.then(
          res => {
            resolve(res)
          },
          reason => {
            reject(reason)
          }
        )
      })
    })
  }

  static all(promiseList) {
    if (promiseList.length === 0) {
      return
    }
    return new MyPromise((resolve, rejcet) => {
      let count = 0
      const resultList = []

      for (let index = 0; index < promiseList.length; index++) {
        const promise = promiseList[index]
        promise.then(
          res => {
            count++
            resultList[index] = res
            if (count === promiseList.length) {
              resolve(resultList)
            }
          },
          reason => {
            reject(reason)
          }
        )
      }
    })
  }
}

/**
 * @param {*} promise 执行回调函数的那个promise
 * @param {*} x 回调函数执行返回值
 * @param {*} resolve promise中的resolve
 * @param {*} reject promise中的reject
 * @param {*} end 这个值在fullfilled中是resolve,在rejected中是reject,在如果x的值是普通类型,将会由end来处理
 */
function handlePromiseEnd(promise, x, resolve, reject, end) {
  // 避免套娃
  if (promise === x) {
    return reject(new Error('error'))
  }

  if (typeof x === 'function') {
    // 如果是方法,执行完,对结果再判断一次
    let rx = x()
    handlePromiseEnd(promise, rx, resolve, reject, end)
  } else if (x instanceof MyPromise) {
    // 如果是promise,end走那条路就交给promise来决定了
    let onResolved = val => {
      handlePromiseEnd(x, val, resolve, reject, resolve)
    }
    let onRejected = reason => {
      handlePromiseEnd(x, reason, resolve, reject, reject)
    }

    // 别问我为什么非要套个微任务,源码就是这样写的,我能怎么办呢
    queueMicrotask(() => {
      x.then(onResolved, onRejected)
    })
  } else {
    // 直到结果是一个普通值,调用end处理
    end(x)
  }
}

最后,我这里并没有添加try catch捕获异常,各位可以自行添加

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值