简易Promise实现

244 阅读3分钟

前言

promise相关问题是很常见的面试题了,最近也在熟悉相关的东西所以趁着这个机会顺便总结一下,写的不好和不对的地方大家多多指教。

什么是promise

Promise 对象用于表示一个异步操作的最终完成(或失败)及其结果值。

简易描述

  1. promise是一个类,它的构造函数接受一个函数,函数的两个参数也是函数;
  2. promise拥有三个状态,pending、fulfilled,rejected,默认是pending状态,成功执行resolve表示,失败执行reject回调,状态一旦更改就不能再次更改;
  3. 存在一个实例方法then,一个参数是成功之后执行的回调函数,一个参数是失败执行的回调函数;
  4. then支持链式调用。

实现

class MyPromise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'
  
  constructor(executor) {
    this.status = MyPromise.PENDING // 默认pending态
    this.value = undefined // 执行成功的值
    this.reason = undefined // 执行失败的值
    this.successAry = []
    this.failAry = []
    executor(this._resolve.bind(this), this._reject.bind(this))
  }

  _resolve (value) {
    // promise状态一旦更改就不能再更改了
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.FULFILLED
      this.value = value
      this.successAry.forEach(cb => cb(this.value))
    }
  }

  _reject (reason) {
    if (this.status === MyPromise.REJECTED) {
      this.status = MyPromise.REJECTED
      this.reason = reason
      this.failAry.forEach(cb => cb(this.reason))
    }
  }

  _then (onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' 
        ? onFulfilled : value => value // .then的回调函数,不是函数的话自己生成一个
     
    onRejected = typeof onRejected === 'function' 
        ? onRejected : reason => reason
        
    return new MyPromise((nextResolve, nextReject) => {
      if (this.status === MyPromise.FULFILLED) {
        let value = onFulfilled(this.value) // 计算出下一个回调的返回值
        nextResolve(value)
      }

      if (this.status === MyPromise.REJECTED) {
        let reason = onRejected(this.reason)
        nextReject && nextReject(reason) // 因为第二个参数大部分时候不传,所以要做一下兼容
      }

      // 考虑到透传的情况,比如 let pr = Promise().resolve(2)     pr.then().then(res => {xxxxxxxxxxxxx})
      if (this.status === MyPromise.PENDING) {
        this.successAry.push(value => {
          let success_value = onfulfilled(value)
          nextResolve(success_value)
        })
        this.failAry.push(reason => {
          let fail_reason = onRejected(reason)
          nextReject && nextReject(fail_reason)
        })
      }
    })
  }
  
  _catch(onRejected) {
      return this._then(undefined, onRejected)
  }
  
  finally (fn) {
       return this._then(fn, fn)
  }
}

测试

image.png

image.png 我们可以看到基本成型了,那么接下来再实现一下它的其他方法

其他方法

  • resolve 其实就是return出一个MyPromise的fulfilled状态
static resolve(value) {
    return new MyPromise((resolve, reject) => {resolve(value)})
}
  • reject 其实就是return出一个MyPromise的rejected状态
static reject(reason) {
    return new MyPromise((resolve, reject) => {reject(reason)})
}
  • all 传入一个数组,只有数组内的状态变成fulfilled状态才会返回所有的结果,如果有一个rejected状态,返回错误信息
static all(promises) {
    return new MyPromise((resolve, reject) => {
        if(!Array.isArray(promises)) throw new Error('参数必须是一个数组')
        let result = [], count = 0
        for (let i = 0; i < promises.length; i++) {
            MyPromise.resolve(promises[i])._then(res => {
              result.push(res)
              if (++count === promises.length) resolve(result)
            })._catch(reason => { reject(reason) })
      }
    })
}
  • race 和all类似,只要其中一个实例状态改变,就返回那个实例状态的返回值
static race(promises) {
    return new MyPromise((resolve, reject) => {
        if(!Array.isArray(promises)) throw new Error('参数必须是一个数组')
        for (let i = 0; i < promises.length; i++) {
            MyPromise.resolve(promises[i])._then(res => {
                resolve(res)
            })._catch(err => { reject(err) })
        }
    })
}
  • allSelected

只有等到所有这些参数实例都返回结果,不管是 fulfilled 还是 rejected,而且该方法的状态只可能变成 fulfilled。此方法与 Promise.all 的区别是 all 无法确定所有请求都结束,因为在 all 中,如果有一个被 Promise 被 rejectedp 的状态就立马变成 rejected,有可能有些异步请求还没走完。

static allSelected (promises) {
    return new MyPromise((resolve, reject) => {
        let result = [], count = 0
        for (let i = 0; i < promises.length; i++) {
            MyPromise.resolve(promises[i]).finally(res => {
                result.push(res)
                ++count === promises.length && resolve(result)
            })
        }
    })
}

结语

写的不好,有错误的地方还请各位大佬多多指教,感恩家人🙏