手写Promise

86 阅读2分钟

v2-a7d68bd578b7f37171b61543f5a2547e_720w.jpg.png

定义私有属性

const MyPromise = (() => {
  // 私有属性用WeakMap定义,放到放到IIFE(立即执行函数)里,形成闭包,做到属性私有
  const status = new WeakMap() // 存放Promise三个状态值
  const value = new WeakMap() // fulfilled存放的值
  const reason = new WeakMap() // rejected存放的值
  const callbacks = new WeakMap() // pending时将then方法onFulfilled和onRejected存放到这,等待fulfilled或rejected时执行
  return class {
    constructor() {
      // 私有属性初始化
      status.set(this, PENDING)
      value.set(this, null)
      reason.set(this, null)
      callbacks.set(this, [])
    }
  }
})()

定义 Promise的三个状态,实例方法和静态方法

const MyPromise = (() => {
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'
  return class {
    then() {} // 后边实现
    catch() {} // 后边实现
    static resolve() {} // 后边实现
    static reject() {} // 后边实现
    static all() {} // 后边实现
    static race() {} // 后边实现
  }
})()

添加constuctor里的执行器

class {
  constuctor(executor/*执行器*/) {
    /* ...私有属性初始化 */
    const resolve = () => {} // 后边实现
    const reject = () => {} // 后边实现
    try {
      executor(resolve, reject)
    } catch(error) {
      reject(error)
    }
  }
}

实现executor执行器的resolve和reject

const resolve = (val) => {
  if(status.get(this) === PENDING) {
    status.set(this, FULFILLED)
    value.set(this, val)
    setTimeout(() => { // 模拟Promise推入任务队列
      const cbs = callbacks.get(this)
      const len = cbs.length
      for(let i = 0; i < len; i++) {
        this.callbacks[i].onFulfilled(value)
      }
    })
  }
}
const resolve = (val) => {
  if(status.get(this) === PENDING) {
    status.set(this, REJECTED)
    reason.set(this, val)
    setTimeout(() => { // 模拟Promise推入任务队列
      const cbs = callbacks.get(this)
      const len = cbs.length
      for(let i = 0; i < len; i++) {
        this.callbacks[i].onRejected(value)
      }
    })
  }
}

这两段代码有点类似,抽象为一个高阶函数,放到IIFE里

const genExecInitFn = (obj, result, settledName) => (val) => {
  if(status.get(obj) === PENDING) {
    status.set(obj, settledName)
    result.set(obj, val)
    setTimeout(() => {
      const cbs = callbacks.get(obj)
      const len = cbs.length
      for(let i = 0; i < len; i++) {
        cbs[i][`on${settledName}`](val)
      }
    })
  }
}

在constuctor里调用这个高阶函数

const resolve = genExecInitFn(this, value, FULFILLED)
const reject =  genExecInitFn(this, reason, REJECTED)

实现then方法

class {
  then(onFulfilled, onRejected) {
    if(this.status === FULFILLED) {
      setTimeout(() => { // 模拟Promise推入任务队列
        onFulfilled(value.get(this)) // 标记1
      })
    }
    if(this.status === REJECTED) {
      setTimeout(() => { // 模拟Promise推入任务队列
        onRejected(reason.get(this)) // 标记2
      })
    }
    if(this.status === PENDING) {
      callbacks.get(obj).push({
        onFulfilled(value) {
          onFulfilled(value.get(this)) // 标记3
        },
        onRejected(reason) {
          onRejected(reason.get(this)) // 标记4
        }
      })
    }
  }
}

这里还有好多细节没实现,比如then方法会返回一个新的Promise

class {
  then(onFulfilled, onRejected) {
    return new MyPromise()
  }
}

比如,如果onFulfilled和onRejected也返回Promise,新的Promise的resolve和reject做为这个Promise的then入参

class {
  then(onFulfilled, onRejected) {
    return new MyPromise((nextFulfilled, nextRejected) => {
      const result = onFulfilled(value.get(this)) // onRejected也是如此
      if(result instanceof MyPromise) {
        result.then(nextFulfilled, nextRejected)
      } else {
        nextFulfilled(result)
      }
    })
  }
}

比如,这个Promise还不能引用循环

class {
  then(onFulfilled, onRejected) {
    const promise = new MyPromise((nextFulfilled, nextRejected) => {
      const result = onFulfilled(value.get(this)) // onRejected也是如此
      if(promise === result) {
        throw new TypeError('Chaining cycle detected for promise')
      }
    })
    return promise
  }
}

以上都是为了能链式调用,标记1到4都要这样,所以封装成一个函数,放到IIFE里

const handlePromise = (promise, result, nextFulfilled, nextRejected) => {
  if(promise === result) {
    throw new TypeError('chaining cycle!')
  }
  try {
    if(result instanceof MyPromise) {
      result.then(nextFulfilled, nextRejected)
    } else {
      nextFulfilled(result)
    }
  } catch(error) {
    nextRejected(error)
  }
}

把then的这一大块也放到一个函数里吧

const run = (obj, onFulfilled, onRejected) => {
  const promise = new MyPromise((nextFulfilled, nextRejected) => {
    if(status.get(obj) === FULFILLED) {
      setTimeout(() => handlePromise(promise, onFulfilled(value.get(obj)), nextFulfilled, nextRejected))
    }
    if(status.get(obj) === REJECTED) {
      setTimeout(() => handlePromise(promise, onRejected(reason.get(obj)), nextFulfilled, nextRejected))
    }
    if(status.get(obj) === PENDING) {
      callbacks.get(obj).push({
        [`on${FULFILLED}`]: () => handlePromise(promise, onFulfilled(value.get(obj)), nextFulfilled, nextRejected),
        [`on${REJECTED}`]: () => handlePromise(promise, onRejected(reason.get(obj)), nextFulfilled, nextRejected)
      })
    }
  })
  return promise
}

还有就是,onFulfilled和onRejected无论如何都要是个函数

const genFn = (fn) => {
  if(typeof fn !== 'function') {
    return value => value
  } else {
    return fn
  }
}

然后完整的then方法如下

class {
  then(onFulfilled, onRejected) {
    onFulfilled = genFn(onFulfilled)
    onRejected = genFn(onRejected)
    return run(this, onFulfilled, onRejected)
  }
}

实现catch方法

这个就比较简单了,复用then的逻辑

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

实现静态方法resolve和reject

这个也比较简单

class {
  static resolve(val) {
    return new MyPromise((resolve, reject) => val instanceof MyPromise? val.then(resolve, reject): resolve(val))
  }
  static reject(val) {
    return new MyPromise((resolve, reject) => reject(val))
  }
}

实现静态方法all和race

首先他们是肯定都返回Promise,都传入Promise数组,all是所有都fulfilled后,这个Promise才fulfilled,而race只要一个就行

const genExecParamsAll = (promises) => (resolve, reject) => {
  try {
    const len = promises.length
    const res = new Array(len).fill(void 0)
    let count = 0
    for(let i = 0; i < len; i++) {
      promises[i].then((val) => {
        res[i] = val
        count = count + 1
        if(count >= len) {
          resolve(res)
        }
      }).catch(reject)
    }
  } catch(error) {
    reject(error)
  }
} 
const genExecParamsRace = (promises) => (resolve, reject) => {
  try {
    const len = promises.length
    for(let i = 0; i < len; i++) {
      promises[i].then(resolve).catch(reject)
    }
  } catch(error) {
    reject(error)
  }
}   
class {
  static all(promises) {
    return new MyPromise(genExecParamsAll(promises))
  }
  static race(promises) {
    return new MyPromise(genExecParamsRace(promises))
  }
}

最后,完整代码如下

const MyPromise = (() => {
  const PENDING = 'pending'
  const FULFILLED = 'fulfilled'
  const REJECTED = 'rejected'

  const status = new WeakMap()
  const value = new WeakMap()
  const reason = new WeakMap()
  const callbacks = new WeakMap()

  const genExecInitFn = (obj, result, settledName) => (val) => {
    if(status.get(obj) === PENDING) {
      status.set(obj, settledName)
      result.set(obj, val)
      setTimeout(() => {
        const cbs = callbacks.get(obj)
        const len = cbs.length
        for(let i = 0; i < len; i++) {
          cbs[i][`on${settledName}`](val)
        }
      })
    }
  }
  const genFn = (fn) => {
    if(typeof fn !== 'function') {
      return value => value
    } else {
      return fn
    }
  }
  const run = (obj, onFulfilled, onRejected) => {
    const promise = new MyPromise((nextFulfilled, nextRejected) => {
      if(status.get(obj) === FULFILLED) {
        setTimeout(() => handlePromise(promise, onFulfilled(value.get(obj)), nextFulfilled, nextRejected))
      }
      if(status.get(obj) === REJECTED) {
        setTimeout(() => handlePromise(promise, onRejected(reason.get(obj)), nextFulfilled, nextRejected))
      }
      if(status.get(obj) === PENDING) {
        callbacks.get(obj).push({
          [`on${FULFILLED}`]: () => handlePromise(promise, onFulfilled(value.get(obj)), nextFulfilled, nextRejected),
          [`on${REJECTED}`]: () => handlePromise(promise, onRejected(reason.get(obj)), nextFulfilled, nextRejected)
        })
      }
    })
    return promise
  }
  const handlePromise = (promise, result, nextFulfilled, nextRejected) => {
    if(promise === result) {
      throw new TypeError('chaining cycle!')
    }
    try {
      if(result instanceof MyPromise) {
        result.then(nextFulfilled, nextRejected)
      } else {
        nextFulfilled(result)
      }
    } catch(error) {
      nextRejected(error)
    }
  }
  const genExecParamsAll = (promises) => (resolve, reject) => {
    try {
      const len = promises.length
      const res = new Array(len).fill(void 0)
      let count = 0
      for(let i = 0; i < len; i++) {
        promises[i].then((val) => {
          res[i] = val
          count = count + 1
          if(count >= len) {
            resolve(res)
          }
        }).catch(reject)
      }
    } catch(error) {
      reject(error)
    }
  } 
  const genExecParamsRace = (promises) => (resolve, reject) => {
    try {
      const len = promises.length
      for(let i = 0; i < len; i++) {
        promises[i].then(resolve).catch(reject)
      }
    } catch(error) {
      reject(error)
    }
  }
  return class {
    constructor(executor) {
      status.set(this, PENDING)
      value.set(this, null)
      reason.set(this, null)
      callbacks.set(this, [])
      const resolve = genExecInitFn(this, value, FULFILLED)
      const reject =  genExecInitFn(this, reason, REJECTED)
      try {
        executor(resolve, reject)
      } catch(error) {
        reject(error)
      }
    }
    then(onFulfilled, onRejected) {
      onFulfilled = genFn(onFulfilled)
      onRejected = genFn(onRejected)
      return run(this, onFulfilled, onRejected)
    }
    catch(onRejected) {
      return this.then(null, onRejected)
    }
    static resolve(val) {
      return new MyPromise((resolve, reject) => val instanceof MyPromise? val.then(resolve, reject): resolve(val))
    }
    static reject(val) {
      return new MyPromise((resolve, reject) => reject(val))
    }
    static all(promises) {
      return new MyPromise(genExecParamsAll(promises))
    }
    static race(promises) {
      return new MyPromise(genExecParamsRace(promises))
    }
  }
})()