阅读 25

手写 Promise 实现

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
  constructor(executor) {
    this.status = PENDING
    this.result = undefined

    this.fulfillReactions = []
    this.rejectReactions = []

    const that = this
    function resolve(value) {
      // if the promise is already resolved, don't do anything
      if (that.status !== PENDING) {
        return
      }
      if (that === value) {
        // can't resolve to the same Promise
        const selfResolutionError = new TypeError('Cannot resolve to self.')
        return reject(selfResolutionError)
      }
      // if is promise
      if (typeof value === 'object' && typeof value.then === 'function') {
        value.then(resolve, reject)
      } else {
        that.status = FULFILLED
        that.result = value

        triggerReactions(that, 'fulfillReactions')
      }
    }
    function reject(value) {
      // if the promise is already resolved, don't do anything
      if (that.status !== PENDING) {
        return
      }

      that.status = REJECTED
      that.result = value

      triggerReactions(that, 'rejectReactions')
    }
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onFulfilled, onRejected) {
    const C = this.constructor

    const promiseCapability = new PromiseCapability(C)
    return performThen(this, onFulfilled, onRejected, promiseCapability)
  }

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

  static resolve(value) {
    // 如果传进来是promise
    if (value instanceof this) {
      return value
    }
    return new MyPromise(function(resolve) {
      resolve(value)
    })
  }

  static reject(value) {
    return new MyPromise(function(resolve, reject) {
      reject(value)
    })
  }
}

function performThen(promise, onFulfilled, onRejected, resultCapability) {
  function createJob(job, value) {
    if (typeof job === 'function') {
      return job(value)
    } else {
      return value
    }
  }

  const fulfillReaction = value => {
    const result = createJob(onFulfilled, value)

    if (typeof result === 'object' && typeof result.then === 'function') {
      result.then(value => {
        resultCapability.resolve(value)
      })
    } else {
      resultCapability.resolve(result)
    }
  }

  const rejectReaction = value => {
    const result = createJob(onRejected, value)
    if (typeof onRejected === 'function') {
      resultCapability.resolve(result)
    } else {
      resultCapability.reject(result)
    }
  }

  switch (promise['status']) {
    case 'pending':
      promise.fulfillReactions.push(fulfillReaction)
      promise.rejectReactions.push(rejectReaction)
      break
    case 'fulfilled':
      triggerReaction(fulfillReaction, promise.result)
      break
    case 'rejected':
      triggerReaction(rejectReaction, promise.result)
      break
  }
  return resultCapability.promise
}

function triggerReactions(promise, type) {
  promise[type].forEach(reaction => {
    triggerReaction(reaction, promise.result)
  })
}

function triggerReaction(reaction, result) {
  setTimeout(() => {
    reaction(result)
  })
}

class PromiseCapability {
  constructor(C) {
    this.promise = new C((resolve, reject) => {
      this.resolve = resolve
      this.reject = reject
    })
  }
}

MyPromise.all = function(promises) {
  const promiseCapability = new PromiseCapability(this)
  const result = []
  let num = promises.length
  promises.forEach((promise, index) => {
    result[index] = undefined
    MyPromise.resolve(promise).then(value => {
      result[index] = value
      num -= 1
      if (num === 0) {
        promiseCapability.resolve(result)
      }
    })
  }, promiseCapability.reject)

  return promiseCapability.promise
}

MyPromise.race = function(promises) {
  const promiseCapability = new PromiseCapability(this)
  promises.forEach(promise => {
    MyPromise.resolve(promise).then(
      promiseCapability.resolve,
      promiseCapability.reject
    )
  })

  return promiseCapability.promise
}

module.exports = MyPromise
复制代码