Promise A+ 标准实现一个未通过用例的Promise🤔

144 阅读3分钟

昨天晚上读了一下 PromiseA+ 标准,首先这是一道非常不错的面试题,同时最近再看JavaScript对与 Control Abstraction Object 相关的内容,一下子就想起了 Promise,于是就尝试自己根据标准尝试一下。

开源社区已经有非常健全的基于 Promise A+ 的实现方案,但是出于学习研究的角度还是自己尝试实现一个,然后跑了一下用例,发现用例大概只能过到 2.3.3.3.1 之后就出现大部分都是错误的情况。 先拆分一下代码:

  1. 数据结构
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

const isFnc = function isFnc(f) {
    return typeof x === 'function'
}

const isThenable = function isThenable(x) {
  const isFn = isFunc(x)
  const isObject = object !== null && typeof object === 'object'
  const hasThenAndIsFun = x.then && typeof isFunc(x.then)

  return (isObject || isFn) && hasThenAndIsFun
}

class $Promise {
    #value = null
    #exception = null
    #reason = null
    #state = PENDING
    #resolveQueue = []
    #rejectQueue = []
    
    constructor(executor) {
      try{
        executor(this.#fulfill, this.#reject)
      } catch(e) {
        this.#reject(e)
      }
    }
    
    #fulfill = (value) => {}
    
    #reject = (reason) => {}
    
    #Resolve(promise, x, promiseState){}
    
    then(onFulfilled, onRejected) {}
}

  1. then 部分的代码
class $Promise {
    //...
    then(onFulfilled, onRejected) {
      const promise2State = {}

      const promise2 = new $Promise((res, rej) => {
        promise2State.res = res
        promise2State.rej = rej
      })

      if (this.#state === FULFILLED) {
        loop(() => {
          if(isFunc(onFulfilled)) {
            try {
              const x = (1, onFulfilled)(this.#value)
              this.#Resolve(promise2, x, promise2State)
            } catch(e) {
              promise2State.rej(e)
            }
          } else {
            promise2State.res(this.#value)
          }
        })
      }
      
      if (this.#state === REJECTED) {
        loop(() => {
          if (isFunc(onRejected)) {
            try {
              const x = (1, onRejected)(this.#reason)
              this.#Resolve(promise2, x, promise2State)
            } catch(e) {
              promise2State.rej(e)
            }
          } else {
            promise2State.rej(this.#reason)
          }
        })
      }
      
      if (this.#state === PENDING) {
        this.#resolveQueue.push([promise2, onFulfilled, promise2State])
        this.#rejectQueue.push([promise2, onRejected, promise2State])
      }

      return promise2
    }
    //...
}
  1. fulfillreject
class $Promise {
    //...
    #fulfill = (value) => {

      if (this.#state !== PENDING)
        return
      
      this.#value = value
      this.#state = FULFILLED

      let len = this.#resolveQueue.length

      while(len--) {
        loop(() => {
          const [promise2, onFulfilled, promise2State] = this.#resolveQueue.shift()
          if(isFunc(onFulfilled)) {
            try {
              const x = (1, onFulfilled)(this.#value)
              this.#Resolve(promise2, x, promise2State)
            } catch(e) {
              promise2State.rej(e)
            }
          } else {
            promise2State.res(this.#value)
          }
        })
      }
    }
    
    #reject = (reason) => {
  
        if (this.#state !== PENDING)
          return
        
        this.#reason = reason
        this.#state = REJECTED
  
        let len = this.#rejectQueue.length
  
        while(len--) {
          loop(() => {

            const [promise2, onRejected, promise2State] = this.#rejectQueue.shift()

            if (isFunc(onRejected)) {
              try {
                const x = (1, onRejected)(this.#reason)
                this.#Resolve(promise2, x)
              } catch(e) {
                promise2State.rej(e)
              }
            } else {
              promise2State.rej(this.#reason)
            }
          })
        }
    }
    //...
}
  1. 最后是 Resolve 部分代码
class $Promise {
    //...
    #Resolve(promise, x, promiseState){
      if (promise === x) {
        const reason = new TypeError()
        promiseState.rej(reason)
        return
      }

      if (x instanceof $Promise) {
        x.then(promiseState.res, promiseState.rej)
      } else if(isFunc(x) || isObject(x)) {
        let then = null
        try {
          then = x.then
        } catch(e) {
          promiseState.rej(e)
        }

        if (isFunc(then)) {
          let settled = false
          try {
            then.apply(
              x,
              [y => {
                if (!settled) {
                  settled = true
                  this.#Resolve(promise, y, promiseState)
                }
              },
              r => {
                if (!settled) {
                  settled = true
                  this.#Resolve(promise, r, promiseState)
                }
              }]
            )
          } catch (e) {
            promiseState.rej(e)
          }
        } else {
          promiseState.res(x)
        }

      } else {
        promiseState.res(x)
      }
    }
    //...
}

如果真的按照标准尝试去实现一个 Promise 是一定会遇到下面的问题的:

  1. then 方法接收的参数的调用时机是什么时候:是在对应的 Promise状态已兑现仅剩与平台相关的代码时调用,简单点说就是 state 的值已经改变且前一个微任务执行完毕之后。虽然是用 宏任务 实现的。
  2. 改变 Promise 状态的途径有哪些:这个问题涉及多个 Promise。第一个是显示创建的,第二个是在代码内部隐式创建的,第三个是作为返回值返回的。虽然有很多 Promise 需要处理,但是只需要记住的是,只有通过 reject, resovle 两种方式才能改变 对应的状态。
  3. 如何处理关联的 Promise 状态:其实此次用例没有过完的主要原因就是没有处理好 Promise 之间的关联状态。Promise 存在一个已解决未兑现的中间状态,就是用一个Promise 最为返回值,此时两个 Promise 的状态是相关联的,而且这个关联出于一个递归的状态。
  4. 错误状态是如何展示的:Promise 的机制是不会再当前结果既处理结果又错误的,所以通常捕获此次执行过程中的错误状态都是通过一个隐式生成的Promise来反馈的

以上几点就是我在实现过程中遇到的需要仔细思考的问题,过段时间会尽可能的补全和解决当前的问题,然后详细的记录一下实现过程