手写实现一个Promise类

239 阅读2分钟

写这篇文章的初衷

在整理promise知识点的过程中,想到之前去外面面试的小伙伴经常会说面试官要求现场写一个promise类,在掘金上没有看到一个简单明了的源码实现,刚好最近刚学习完继承与原型链相关知识,趁热打铁试试手撕promise功能。

class MyPromise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECT = 'reject'
  constructor(executor) {
    this.status = MyPromise.PENDING
    this.value = undefined
    this.callbacks = [] // 存放需要回调的函数
    // 捕获实例化传入方法在执行时出现错误异常
    try {
      // resolve方法没有this指向,需要传入this指向。
      executor(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }
  
  resolve (value) {
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.FULFILLED
      this.value = value
      // 实现then中的回调是异步执行
      setTimeout(() => {
        this.callbacks.map(item => {
          item.onFulfilled(value)
        })
      })
    }
  }

  reject (reason) {
    if (this.status === MyPromise.PENDING) {
      this.status = MyPromise.REJECT
      this.value = reason
      // 实现then中的回调是异步执行
      setTimeout(() => {
        this.callbacks.map(item => {
          item.onRejected(reason)
        })
      })
    }
  }

  then (onFulfilled, onRejected) {
    // 解决传入的不为function
    if (typeof onFulfilled !== 'function') {
      // 解决then()不传值时的穿透传递问题
      onFulfilled = (value)=> value
    }
    if (typeof onRejected !== 'function') {
      onRejected = (reason)=> {throw reason}
    }
   
    // 返回一个新的promise解决链式调用
    let myPromise = new MyPromise((resolve, reject) => {
      // 解决resolve与reject被放在异步方法进行操作
      if (this.status === MyPromise.PENDING) {
        this.callbacks.push({
          onFulfilled: value => {
            this.parse(myPromise, onFulfilled, value, resolve, reject)
          },
          onRejected: value => {
            this.parse(myPromise, onRejected, value, resolve, reject)
          },
        })
      }

      if (this.status === MyPromise.FULFILLED) {
        // then的回调函数都是异步的,所以使用setTimeout放在异步队列里
        setTimeout(() => {
          this.parse(myPromise, onFulfilled, this.value, resolve, reject)
        })
      }
  
      if (this.status === MyPromise.REJECT) {
        setTimeout(() => {
          this.parse(myPromise, onRejected, this.value, resolve, reject)
        })
      }
    })
    return myPromise
  }

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

  /**
   * 处理过程公共代码提取
   * @param {*} myPromise 新promise
   * @param {*} callback 回调函数
   * @param {*} value 返回数值
   * @param {*} resolve 成功函数
   * @param {*} reject  失败函数
   */
  parse (myPromise, callback, value, resolve, reject) {
    // 捕获reject状态下的then()穿透
    try {
      var result = callback(value) // var起到跨越块级作用域
    } catch (error) {
      reject(error)
    }
     // 限制return返回自身
     if (myPromise === result) {
      throw TypeError('Chaining cycle detected for promise')
    } 
    // 解决方法在调用时出现错误异常
    try {
      // return 返回的是一个promise,此时只需要获取内部的值即可
      if (result instanceof MyPromise) {
        result.then(resolve,reject)
      } else {
        // return出来的值一定由新promise的resolve进行接收
        resolve(result)
      }
    } catch (error) {
      // 错误异常的结果一定由新promise的reject进行接收
      reject(error)
    }
  }

  static resolve (value) {
    return new MyPromise((resolve, reject) => {
      try {
        // return 返回的是一个promise,此时只需要获取内部的值即可
        if (value instanceof MyPromise) {
          value.then(resolve,reject)
        } else { 
          // return出来的值一定由新promise的resolve进行接收
          resolve(value)
        }
      } catch (error) {
        // 错误异常的结果一定由新promise的reject进行接收
        reject(error)
      }
    })
  }

  static reject (value) {
    return new MyPromise((resolve, reject) => {
      reject(value)
    })
  }
  
  static all(promises) {
    const values = []
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise,index) => {
        promise.then(
          value => {
            values[index] = value
            if(values.length === promises.length) {
              resolve(values)
            }
          },
          reason => {
            reject(reason)
          }
        )
      })
    })
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(
          value => {
            resolve(value)
          },
          reason => {
            reject(reason)
          }
        )
      })
    })
  }
}