promise

26 阅读1分钟
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

const Promise = function (executor) {
  if (!(executor instanceof Function)) {
    throw 'executor must be a function'
  }
  this.state = PENDING
  this.result = undefined
  this.callback = []
  const _onResolve = function (value) {
    if (this.state !== PENDING) return
    this.state = FULFILLED
    this.result = value
    if (this.callback.length > 0) {
      this.callback.forEach(item => {
        item.onResolved()
      })
    }
  }
  const _onReject = function (value) {
    if (this.state !== PENDING) return
    this.state = REJECTED
    this.result = value
    if (this.callback.length > 0) {
      this.callback.forEach(item => {
        item.onRejected()
      })
    }
  }
  try {
    executor(_onResolve.bind(this), _onReject.bind(this))
  } catch (error) {
    _onReject.call(this, error)
  }
}

Object.assign(Promise.prototype, {
  then(onResolved, onRejected) {
    // onResolved不是函数,成功回调默认值 value => value
    if (!(onResolved instanceof Function)) {
      onResolved = value => value
    }
    // onRejected不是函数,失败回调默认值 resason => throw reason
    if (!(onRejected instanceof Function)) {
      onRejected = reason => {
        throw reason
      }
    }

    // this =>> p
    return new Promise(
      function (resolve, reject) {
        const _common = function (cb) {
          setTimeout(() => {
            try {
              const value = cb(this.result)
              if (value instanceof Promise) {
                value.then(resolve, reject)
              } else {
                resolve(value)
              }
            } catch (error) {
              reject(error)
            }
          })
        }
        if (this.state === FULFILLED) {
          _common.call(this, onResolved)
        } else if (this.state === REJECTED) {
          _common.call(this, onRejected)
        } else {
          this.callback.push({
            onResolved: _common.bind(this, onResolved),
            onRejected: _common.bind(this, onRejected)
          })
        }
      }.bind(this)
    )
  },
  catch(onRejected) {
    return this.then(undefined, onRejected)
  }
})

export default Promise