手写promise

一个实现了Promise A+规范的promise
采用promises-aplus-tests包测试通过
测试过程中报过很多错,本来以为是对于规范的实现有问题,后来发现是代码在运行过程中报错导致测试通不过
之后用vscode调试了代码后就很容易发现问题,所以推荐把promises-aplus-tests安装在项目下,方便调试

问题如下
  1. 其中一个问题是x.constructor === promise.constructor判断时,没有考虑xundefined时会报错
  2. 之前把resolvereject方法定义在constructor下时,在then方法里创建新promise实例,然后需要根据不同情况来改变子promise状态。此时想要把这块逻辑封装起来时会发现,子promise是拿不到的,因为还未完成变量赋值,除非用setTimeout把代码改成异步
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const PROMISE_ID = Math.random().toString(36).substring(2)

function isObjectOrFunction(x) {
  let type = typeof x
  return x !== null && (type === 'object' || type === 'function')
}

function isFunction(x) {
  return typeof x === 'function'
}

let id = 0
function nextId() {
  return id++
}

const resolvePromise = (promise, x) => {
  if (promise === x) {
    return reject(promise, new TypeError('Chaining cycle'))
  }
  if (x && x.constructor === promise.constructor) {
    // 两种写法 x instanceof MyPromise 和 x.constructor === promise.constructor;需要注意的是:
    // 后一种必须判断x是否存在 否则会报constructor of undefined的错误
    x.then((v) => {
      resolvePromise(promise, v)
    }, (e) => {
      reject(promise, e)
    })
  } else if (isObjectOrFunction(x)) {
    // 函数或对象
    let used = false
    let then = void 0
    try {
      then = x.then
    } catch (e) {
      return reject(promise, e)
    }
    if (isFunction(then)) {
      try {
        then.call(x, (v) => {
          if (used) return
          used = true
          resolvePromise(promise, v)
        }, (r) => {
          if (used) return
          used = true
          reject(promise, r)
        })
      } catch (e) {
        if (used) return
        used = true
        reject(promise, e)
      }
    } else {
      resolve(promise, x)
    }
  } else {
    // 普通值
    resolve(promise, x)
  }
}

function noop() {}

function flushSchedule(func) {
  setTimeout(func, 0)
}

function settlePromise(promise, callback, arg) {
  return () => {
    let x = void 0
    try {
      x = callback(arg)
    } catch (e) {
      return reject(promise, e)
    }
    resolvePromise(promise, x)
  }
}

function resolve (promise, value) {
  if (promise.state === PENDING) {
    promise.state = FULFILLED
    promise.value = value
    promise.onFulfilledCallbacks.forEach(fn => fn())
  }
}

function reject (promise, reason) {
  if (promise.state === PENDING) {
    promise.state = REJECTED
    promise.reason = reason
    promise.onRejectedCallbacks.forEach(fn => fn())
  }
}

class MyPromise {
  state = PENDING
  value = undefined
  reason = undefined
  onFulfilledCallbacks = []
  onRejectedCallbacks = []
  constructor(executor) {
    this[PROMISE_ID] = nextId()
    try {
      executor((value) => {
        resolve(this, value);
      }, (reason) => {
        reject(this, reason)
      })
    } catch (e) {
      reject(this, e)
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
    let child = new this.constructor(noop)

    if (this.state === FULFILLED) {
      flushSchedule(settlePromise(child, onFulfilled, this.value))
    } else if (this.state === REJECTED) {
      flushSchedule(settlePromise(child, onRejected, this.reason))
    } else if (this.state === PENDING) {
      this.onFulfilledCallbacks.push(() => {
        flushSchedule(settlePromise(child, onFulfilled, this.value))
      })
      this.onRejectedCallbacks.push(() => {
        flushSchedule(settlePromise(child, onRejected, this.reason))
      })
    }

    return child
  }
}

// promises-aplus-tests测试MyPromise的测试代码
MyPromise.defer = MyPromise.deferred = function () {
  let dfd = {}
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

module.exports = MyPromise

你可能感兴趣的:(手写promise)