toFrankie / blog

种一棵树,最好的时间是十年前。其次,是现在。
20 stars 1 forks source link

手写实现 Promise/A+ 标准 #198

Open toFrankie opened 1 year ago

toFrankie commented 1 year ago

本文仅作为个人记录,文中可能存在不严谨的地方。

要了解更多可以看下这两篇文章:

Promise/A+ 的标准有哪些?

具体代码实现

function MyPromise(executor) {
  const _this = this
  // 状态
  _this.status = 'pending'
  // resolve 值
  _this.value = null
  // reject 原因
  _this.reason = null
  // resolve、reject 函数
  _this.onFullfilled = []
  _this.onRejected = []

  function resolve(value) {
    if (value instanceof MyPromise) {
      return value.then(resolve, reject)
    }

    // 其实这里采用 setTimeout 方式实现异步执行 onFullfilled/onRejected 不太符合 Event Loop 机制。下面 reject 同理。
    setTimeout(() => {
      // 只有状态为 pending 才能被改变
      if (_this.status == 'pending') {
        _this.value = value
        _this.status = 'resolved'
        _this.onFullfilled.forEach(currentValue => currentValue(value))
      }
    }, 0)
  }

  function reject(reason) {
    setTimeout(() => {
      // 只有状态为 pending 才能被改变
      if (_this.status == 'pending') {
        _this.reason = reason
        _this.status = 'rejected'
        _this.onRejected.forEach(currentValue => currentValue(reason))
      }
    }, 0)
  }

  // 注意:若执行过程出现异常,则捕获异常并执行 reject 函数。
  try {
    executor(resolve, reject)
  } catch (e) {
    reject(e)
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  let then
  let thenCallorThrow = false

  if (promise2 === x) {
    return reject(new TypeError('same Promise!'))
  }

  if (x instanceof MyPromise) {
    if (x.status === 'pending') {
      x.then(value => {
        resolvePromise(promise2, value, resolve, reject)
      }, reject)
    } else {
      x.then(resolve, reject)
    }
    return
  }

  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      then = x.then
      if (typeof then === 'function') {
        then.call(
          x,
          res => {
            if (thenCallorThrow) return
            thenCallorThrow = true
            return resolvePromise(promise2, res, resolve, reject)
          },
          err => {
            if (thenCallorThrow) return
            thenCallorThrow = true
            return reject(err)
          }
        )
      } else {
        resolve(x)
      }
    } catch (e) {
      if (thenCallorThrow) return
      thenCallorThrow = true
      return reject(e)
    }
  } else {
    return resolve(x)
  }
}

MyPromise.prototype.then = function (onFullfilled, onRejected) {
  const _this = this
  let promise2 // promise.then() 返回一个 promise 对象

  // Promise 值的穿透处理:
  // 场景如: new Promise(resolve => resolve('abc')).then().catch().then(res => {console.log('print abc')})
  onFullfilled = typeof onFullfilled === 'function' ? onFullfilled : val => val
  onRejected =
    typeof onRejected === 'function'
      ? onRejected
      : err => {
          throw err
        }

  switch (_this.status) {
    case 'pending':
      promise2 = new MyPromise((resolve, reject) => {
        _this.onFullfilled.push(value => {
          try {
            const x = onFullfilled(value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
        _this.onRejected.push(reason => {
          try {
            const x = onRejected(reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      })
      break
    case 'resolved':
      promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            const x = onFullfilled(_this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      })
      break
    case 'rejected':
      promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            const x = onRejected(_this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      })
      break
    default:
      break
  }

  return promise2
}

// Promise 标准里面没有 catch、race、all 等方法,只有一个 then 方法
MyPromise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected)
}

new MyPromise((resolve, reject) => {
  resolve('right')
}).then(
  res => {
    console.log(res)
  },
  err => {
    console.warn(err)
  }
)

未完待续...