跳至主要內容

24. Promise

鸭梨大约 1 分钟

24. Promise

1. 不考虑异步情况的 Promise

// 首先是不考虑异步情况的话
export class Promise {
  constructor(executor) {
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    const resolve = (value) => {
      // 成功回调
      if (this.state === 'pending') {
        this.state = 'fullfilled'
        this.value = value
      }
    }
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
      }
    }
    try {
      executor(resolve, reject)
    }
    catch (error) {
      reject(error)
    }
  }

  // then 方法有两个参数,表示成功函数和失败函数
  // 把获取到的值传入到对应的函数
  then(onFinished, onRejected) {
    // if (this.state === "pending") {
    // }
    if (this.state === 'fullfilled')
      onFinished(this.value)

    if (this.state === 'rejected')
      onRejected(this.reason)
  }
}

2. 考虑异步情况的 Promise

我们按照 setTimeout 为例,setTimeOut(function, delay, args...)

  • 函数名:那么等 delay 以后再去调用函数
  • 函数:那么直接会调用函数
  • 箭头函数包裹着的函数:等待 delay 以后调用
// 考虑异步情况的 Promise
export class Promise {
  constructor(excutor) {
    // 状态
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    // 回调函数记录
    this.OnResolvedCallbacks = []
    this.OnRejectedCallbacks = []
    // 成功回调
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fullfilled'
        this.value = value
        this.OnResolvedCallbacks.forEach(fn => fn())
      }
    }
    // 失败回调
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.OnRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      excutor(resolve, reject)
    }
    catch (error) {
      reject(error)
    }
  }

  then(onFinished, onRejected) {
    if (this.state === 'pending') {
      // 以 setTimeout 为例,就是 delay 之前,不知道状态
      this.OnResolvedCallbacks.push(() => {
        onFinished(this.value)
      })
      this.OnRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
    if (this.state === 'rejected')
      onRejected(this.reason)

    if (this.state === 'fullfilled')
      onFinished(this.value)
  }
}

3. 考虑 Promise 链式调用情况

为了解决 Promise 的链式调用,所以第一次 then 的返回值是一个 Promise 对象,后续的每次 then,都需要借助上一个的返回值,如果没有那么就是 undefined

// 解决链式调用,也就是第一次 then 的返回值是一个 Promise 对象
export class Promise {
  constructor(excutor) {
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    // 数组存放状态函数回调
    this.OnResolvedCallbacks = []
    this.OnRejectedCallbacks = []
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fullfilled'
        this.value = value
        this.OnRejectedCallbacks.forEach(fn => fn())
      }
    }
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.OnRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      excutor(resolve, reject)
    }
    catch (error) {
      reject(error)
    }
  }

  then(onFinished, onRejected) {
    // 第一次会返回一个 Promise
    const Promise2 = new Promise((resolve, reject) => {
      if (this.state === 'pending') {
        // 成功
        this.OnResolvedCallbacks.push(() => {
          const x = onFinished(this.value)
          resolvePromise(Promise2, x, resolve, reject)
        })
        // 失败
        this.OnRejectedCallbacks.push(() => {
          const x = onRejected(this.reason)
          resolvePromise(Promise2, x, resolve, reject)
        })
      }
      // 成功状态
      if (this.state === 'fullfilled') {
        const x = onFinished(this.value)
        resolvePromise(Promise2, x, resolve, reject)
      }
      // 失败状态
      if (this.state === 'rejected') {
        const x = onRejected(this.reason)
        resolvePromise(Promise2, x, resolve, reject)
      }
    })
    return Promise2
  }
}

4. 实现 resolvePromise

我们来实现一下,resolvePromise函数,其用于判断返回值:

  • x === promise2 则错误
  • x 的类型不是函数也不是 Object,那么就直接进入 resolve
  • 否则的话,判断 xthen 类型,如果为函数,那么一定是 Promise,然后判断这个时成功还是失败
  • 否则的话,直接 resolve
// resolvePromise 就是用来判断返回值是什么的
// x === promise2 => 错误
// x 的类型不是函数也不是 Object,那么就直接进入 resolve
// 否则的话,判断 x的 then 类型,如果为函数,那么一定是 Promise,然后判断这个时成功还是失败
// 否则的话,直接 resolve
export const resolvePromise = function (Promise2, x, resolve, reject) {
  if (x === Promise2) {
    // 直接报错
    return reject(new TypeError('Chaining cycle detected for promise'))
  }
  // 防止多次调用
  let called = false
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // 那么可能是 Promise
      // 接下来判断 x.then 如果为 func,那么一定是 Promise
      const then = x.then
      if (typeof then === 'function') {
        // 那么一定是 Promise
        // this 指向 x
        then.call(x, (y) => {
          // 成功回调
          if (called)
            return

          called = true
          resolvePromise(Promise2, y, resolve, reject)
        }, (err) => {
          // 失败
          if (called)
            return
          called = true
          resolvePromise(Promise2, err, resolve, reject)
        })
      }
    }
    catch (error) {
      // 如果调用过来,直接返回
      if (called)
        return
      called = true
      reject(error)
    }
  }
  else {
    resolve(x)
  }
}

5. 完整 Promise 版本

因为 then 的两个参数 onFinishedonRejected

  • 不是必须的,如果是非函数,那么需要被忽略
  • 不是同步的,必须是被异步调用的,这就需要用 setTimeout 包裹,并且使用 try-catch 进行错误捕捉
export class Promise {
  constructor(executor) {
    this.state = 'pending'
    this.value = undefined
    this.reason = undefined
    this.onResolvedCallbacks = []
    this.onRejectedCallbacks = []
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    }
    catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    // onFulfilled 如果不是函数,就忽略 onFulfilled,直接返回 value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    // onRejected 如果不是函数,就忽略 onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function'
      ? onRejected
      : (err) => {
          throw err
        }
    const promise2 = new Promise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        // 异步
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          }
          catch (e) {
            reject(e)
          }
        }, 0)
      };
      if (this.state === 'rejected') {
        // 异步
        setTimeout(() => {
          // 如果报错
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          }
          catch (e) {
            reject(e)
          }
        }, 0)
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          // 异步
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            }
            catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          // 异步
          setTimeout(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            }
            catch (e) {
              reject(e)
            }
          }, 0)
        })
      };
    })
    // 返回 promise,完成链式
    return promise2
  }
}