手写Promise

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

// 手写promise
function myPromise(excutor) {
  let self = this
  self.value = null
  self.error = null
  self.onfulfilledCallBack = []
  self.onrejectedCallBack = []
  self.status = PENDING

  const resolve = (value) => {
    if (self.status != PENDING) return;
    setTimeout(() => {
      self.status = FULFILLED
      self.value = value
      self.onfulfilledCallBack.forEach(callback => callback(self.value))
    })
  }

  const reject = (error) => {
    if (self.status != PENDING) return;
    setTimeout(() => {
      self.status = REJECTED
      self.error = error
      self.onrejectedCallBack.forEach(callback => callback(self.error))
    })
  }

  excutor(resolve, reject)
}

// then
myPromise.prototype.then = function (onfulfilled, onrejected) {
  // 校验参数
  onfulfilled = typeof onfulfilled == 'function' ? onfulfilled : value => value
  onrejected = typeof onrejected == 'function' ? onrejected : error => {
    throw error
  }
  let bridgePromise
  let self = this
  if (self.status == PENDING) {
    return bridgePromise = new myPromise((resolve, reject) => {
      self.onfulfilledCallBack.push((value) => {
        try {
          let x = onfulfilled(value)
          //   resolvePromise
          resolvePromise(bridgePromise, x, resolve, reject)
          resolve(x)
        } catch (e) {
          reject(e)
        }
      })
      // 同理
      self.onrejectedCallBack.push((error) => {
        try {
          let x = onrejected(error)
          //   resolvePromise
          resolvePromise(bridgePromise, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      })
    })
  } else if (self.status == FULFILLED) {
    return bridgePromise = new myPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          let x = onfulfilled(self.value)
          resolvePromise(bridgePromise, x, resolve, reject)
        } catch (e) {
          reject(e)
        }
      })
    })
  } else {
    setTimeout(() => {
      try {
        let x = onrejected(self.error)
        resolvePromise(bridgePromise, x, resolve, reject)
      } catch (e) {
        reject(e)
      }
    })
  }
  return this
}
// catch 
myPromise.prototype.catch = function (onrejected) {
  return this.then(null, onrejected)
}

function resolvePromise(bridgePromise, x, resolve, reject) {
  // 判断是否promise
  if (x instanceof myPromise) {
    // 是 则继续resolve
    if (x.status == PENDING) {
      x.then(y => {
        resolvePromise(bridgePromise, y, resolve, reject)
      }, error => {
        reject(error)
      })
    } else {
      x.then(resolve, reject)
    }
  } else {
    // 非promise 直接返回
    resolve(x)
  }
}


// promise 的resolve方法
Promise.resolve = function (params) {
  // 判断是否promise
  if (params instanceof Promise) {
    return params
  }
  return new Promise((resolve, reject) => {
    if (params && params.then && typeof params.then == 'function') {
      params.then(resolve, reject)
    } else {
      resolve(params)
    }
  })
}
// promise reject
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason)
  })
}
// promise finally
Promise.prototype.finally = function (callback) {
  this.then(value => {
    return Promise.resolve((callback()).then(() => {
      return value
    }, error => {
      return new Promise.resolve((callback()).then(() => {
        throw error
      }))
    }))
  })
}

// promise all
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let result = [];
    let index = 0;
    let len = promises.length;
    if (len === 0) {
      resolve(result);
      return;
    }
    for (let i = 0; i < len; i++) {
      // 为什么不直接 promise[i].then, 因为promise[i]可能不是一个promise
      Promise.resolve(promise[i]).then(data => {
        result[i] = data;
        index++;
        if (index === len) resolve(result);
      }).catch(err => {
        reject(err);
      })
    }
  })
}

// promise race
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    let len = promises.length;
    if (len === 0) return;
    for (let i = 0; i < len; i++) {
      Promise.resolve(promise[i]).then(data => {
        resolve(data);
        return;
      }).catch(err => {
        reject(err);
        return;
      })
    }
  })
}