Promise

Promise A+ 规范实现

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

// 该方法核心就是判断x
const resolvePromise = (promise2, x, resolve, reject) => {
  // 判断x和promise2是不是同一个人,是则就报错
  if (promise2 === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }
  let called // 内部测试的时候  成功和失败都调用
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(
          x,
          (y) => {
            // y 可能还是一个promise,递归调用,直到解析为一个普通值为止
            if (called) {
              return
            }
            called = true
            resolvePromise(promise2, y, resolve, reject)
          },
          (r) => {
            if (called) {
              return
            }
            called = true // 防止多次调用成功和失败
            reject(r)
          }
        )
      } else {
        resolve(x) // {then: 1} 说明x 是一个普通对象,直接成功即可
      }
    } catch (error) {
      if (called) {
        return
      }
      called = true
      reject(error)
    }
  } else {
    // x 不是对象或函数 肯定是一个普通值
    resolve(x)
  }
}

class Promise {
  constructor(executor) {
    // 不能相信用户的输入,一定要进行参数校验
    if (typeof executor !== 'function') {
      throw new TypeError(` Promise resolver ${executor} is not a function `)
    }
    this.status = PENDING

    this.value = undefined // 成功的值
    this.reason = undefined // 失败的原因

    this.onResolvedCallbacks = [] // 成功的回调的数组
    this.onRejectedCallbacks = [] // 失败的回调的数组

    // 成功函数
    let resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = FULFILLED
        this.onResolvedCallbacks.forEach((fn) => fn()) // 发布
      }
    }

    // 失败函数
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onRejectedCallbacks.forEach((fn) => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }

  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : (val) => val

    onrejected =
      typeof onrejected === 'function'
        ? onrejected
        : (err) => {
            throw err
          }

    let promise2 = new Promise((resolve, reject) => {
      // 同步情况
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            let x = onfulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onrejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      }

      // 异步情况
      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })

    return promise2
  }
}

Promise.defer = Promise.deferred = function() {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

module.exports = Promise

通过第三方依赖包来检测编写的 Promise 是否符合规范:

npm install promises-aplus-tests
promises-aplus-tests ./myPromise.js

常见问题:

  • 为什么要加定时器?

    因为 JS 执行机制,new Promise 没有执行完就传 promise2,报 promise2 未定义

  • 为什么在处理异步情况的时候也要加 try-catch?

    加了异步 setTimeout 后;若代码里直接 throw new Error 抛错的话,上面的异常处理捕捉不到,因为是同步的,我们加了 setTimeout 后该部分就变成异步的了,所以我们需要在处理异步的情况时候也要加个异常处理 try catch