3. Promise常考的三个问题

73 阅读3分钟

一. 手写一下promise

思路

promise.png

核心点

因为每天都在使用promise,所以从promise的使用形式来思考该怎么封装promise

形式上:

  1. 入参为一个立即执行的函数,且函数有两个方法resolve和reject可以调用
  2. 调用resolve会执行then里面的方法,调用reject会执行catch(或者是then里面的第二个参数的方法)
  3. 因为是链式操作,做所以then、catch等方法要么返回this,要么返回对应的promise来完成链式操作,如果是this,会同时调用then和catch,所以这个地方选择返回promise来完成链式操作

使用上:

  1. 有三种状态,分别是pendding、fulfilled、rejected
  2. 异步操作,所以需要有tasklist的数组来完成

由这些点出发,来一一实现,开始coding

代码

1. 完成立即执行,同步调用

promise-sync.png

const STATUS = {
  PENDDING: 'pendding',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class myPromise {
  constructor(rightnow) {
    this.status = STATUS.PENDDING
    this.value = undefined
    this.reason = undefined
    const resolve = value => {
      if (this.status = STATUS.PENDDING) {
        this.status = STATUS.FULFILLED
        this.value = value
      }
    }
    const reject = reason => {
      if (this.status = STATUS.PENDDING) {
        this.status = STATUS.REJECTED
        this.reason = reason
      }
    }
    try {
      rightnow(resolve, reject)
    } catch (error) {
      throw error
    }
  }

  then(onResolved, onRejected) {
    const actions = {
      [STATUS.FULFILLED]: () => {
        onResolved(this.value)
      },
      [STATUS.REJECTED]: () => {
        onRejected(this.reason)
      }
    }
    actions[this.status]?.()
    return this
  }

  catch(onRejected) {
    onRejected(this.reason)
    return this
  }
}

2. 实现异步调用

promise-async.png

const STATUS = {
  PENDDING: 'pendding',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class myPromise {
  constructor(rightnow) {
    this.status = STATUS.PENDDING
    this.value = undefined
    this.reason = undefined
    this.onRejectedCBs = []
    this.onResolvedCBs = []
    const resolve = value => {
      if (this.status = STATUS.PENDDING) {
        this.status = STATUS.FULFILLED
        this.value = value
        this.onResolvedCBs.forEach(fn => fn())
      }
    }
    const reject = reason => {
      if (this.status = STATUS.PENDDING) {
        this.status = STATUS.REJECTED
        this.reason = reason
        this.onRejectedCBs.forEach(fn => fn())
      }
    }
    try {
      rightnow(resolve, reject)
    } catch (error) {
      throw error
    }
  }

  then(onResolved, onRejected) {
    const actions = {
      [STATUS.FULFILLED]: () => {
        onResolved(this.value)
      },
      [STATUS.REJECTED]: () => {
        onRejected(this.reason)
      },
      [STATUS.PENDDING]: () => {
        this.onRejectedCBs.push(() => {
          onRejected(this.reason)
        })
        this.onResolvedCBs.push(() => {
          onResolved(this.value)
        })
      }
    }
    actions[this.status]?.()
    return this
  }

  catch(onRejected) {
    return this
  }
}

3. 实现链式操作

promise-then.png

const STATUS = {
  PENDDING: 'pendding',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
}

class myPromise {
  constructor(rightnow) {
    this.status = STATUS.PENDDING
    this.value = undefined
    this.reason = undefined
    this.onRejectedCBs = []
    this.onResolvedCBs = []
    const resolve = value => {
      if (this.status = STATUS.PENDDING) {
        this.status = STATUS.FULFILLED
        this.value = value
        this.onResolvedCBs.forEach(fn => fn())
      }
    }
    const reject = reason => {
      if (this.status = STATUS.PENDDING) {
        this.status = STATUS.REJECTED
        this.reason = reason
        this.onRejectedCBs.forEach(fn => fn())
      }
    }
    try {
      rightnow(resolve, reject)
    } catch (error) {
      throw error
    }
  }

  then(onResolved, onRejected) {
    const re_promise =  new myPromise((resolve, reject) => {
      const actions = {
        [STATUS.FULFILLED]: () => {
          const x = onResolved(this.value)
          /**
           * resolvePromise需要满足如下含义
           * 1. 如果x为re_promise本身直接跳reject,否则无限循环
           * 2. 如果x不为promise,直接将x的值resolve到下一个then中去
           * 3. 如果x位reject,直接返回reject到下一个catch中去
           */
          resolvePromise(re_promise, x, resolve, reject)
        },
        [STATUS.REJECTED]: () => {
          const x = onRejected(this.reason)
          resolvePromise(re_promise, x, resolve, reject)
        },
        [STATUS.PENDDING]: () => {
          this.onRejectedCBs.push(() => {
            const x = onRejected(this.reason)
            resolvePromise(re_promise, x, resolve, reject)
          })
          this.onResolvedCBs.push(() => {
            const x = onResolved(this.value)
            resolvePromise(re_promise, x, resolve, reject)
          })
        }
      }
      actions[this.status]?.()
    })
    return re_promise
  }

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

/**
 * 
 * @param {Promise} promise 返回的promise
 * @param {any} x 前一个then的return值
 * @param {function} resolve promise的resolve
 * @param {function} reject promise的reject
 * @returns Promise
 */
const resolvePromise = (promise, x, resolve, reject) => {
  if (promise === x) {
    return reject(new Error('then中的方法返回的promise和构造函数一样,会无限循环'))
  }
  let caller = false
  if (isPromiseLike(x)) {
    try {
      const then = x.then
      then.call(x, (y) => {
        if (caller) return
        caller = true
        resolvePromise(promise, y, resolve, reject)
      }, err => {
        if (caller) return
        caller = true
        reject(err)
      })
    } catch (error) {
      if (caller) return
      caller = true
      reject(error)
    }
  } else {
    resolve(x)
  }
}

// note: Promise A+ 规范: https://promisesaplus.com/
const isPromiseLike = p => p !== null && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function'

new myPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('resolve')
  }, 1000)
  setTimeout(() => {
    reject('reject')
  }, 3000)
}).then((res => {
  console.log('res------>', res)
}), (err) => {
  console.log('err------>', err)
})

二. 实现Promise.all方法

思路

promise-all.png

核心点

  1. promise.all的调用入参是一个数组(准确来讲是迭代器)
  2. 如果所有的promise都返回成功,则执行then,有一个失败则执行catch

根据这两点,写代码

代码

if(!Promise.all) {
  Promise.prototype.all = function(promises) {
    return new Promise((resolve, reject) => {
      let result = []
      let count = 0
      let fulfillCount
      // note: 因为是迭代器,所以这个地方用forof更加严谨一些
      for (const item of promises) {
        const index = count
        count++
        Promise.resolve(item).then(res => {
          result[index] = res
          fulfillCount++
          // 因为Promise是微任务,同步的for执行完毕之后才会执行Promise
          if(fulfillCount === count) {
            resolve(result)
          }
        }, reject)
      }
    })
  }
}

三. isPromiseLike(p)(判断p是否是一个类Promise)

这个问题主要考察的是你是否了解Promise A+规范:promisesaplus.com/

这个问题出的挺好的,代码并不难,但是考点很明确

image.png

所以代码就很简单了

const isPromiseLike = p => p !== null && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function'