手写 Promise(超完整详细)

76 阅读1分钟

附上一份自以为比较完整的 promise 实现代码,可根据 promise/A+ 规范自行测试,大部分代码源于方应杭老师(可参考:视频课程)的教程,其中做了一些优化调整,若道友发现有何不妥之处,欢迎留言讨论,期待能实现得更加完美!

class Promise2 {
    #state = 'pending'
    #callbacks = []
    #outerData = undefined
    constructor(fn) {
        if (typeof fn !== 'function') {
            throw new Error('必须传入一个函数')
        }
        fn(this.#resolve.bind(this), this.#reject.bind(this))
    }
    #executeCallbacks() {
        if (this.#state === 'pending') return
        nextTick(() => {
          this.#callbacks.forEach(handle => {
            const hash = {
              fulfilled: 0,
              rejected: 1
            }
            const specificIndex = hash[this.#state]
            if (typeof handle[specificIndex] === 'function') {
              let x
              try {
                x = handle[specificIndex].call(undefined, this.#outerData)
              } catch (error) {
                return handle[2].reject(error)
              }
              handle[2].#resolveWith(x)
            } else {
              handle[2].#resolveWith()
            }
          })
        })
      }
    #resolve(result) {
        if (this.#state !== 'pending') {
            return
        }
        this.#state = 'fulfilled'
        this.#outerData = result
        this.#executeCallbacks()
    }
    #reject(reason) {
        if (this.#state !== 'pending') {
            return
        }
        this.#state = 'rejected'
        this.#outerData = reason
        this.#executeCallbacks()
    }
    then(succeed, fail) {
        const tempArr = []
        if (typeof succeed === 'function') {
            tempArr[0] = succeed
        }
        if (typeof fail === 'function') {
            tempArr[1] = fail
        }
        tempArr[2] = new Promise2(() => { })
        this.#callbacks.push(tempArr)
        this.#executeCallbacks()
        return tempArr[2]
    }
    // 这里的 this 是在 then 里面返回的handle[2],传的是空函数,没有任何状态,因此这里要把 x 的状态传给 this
    #resolveWith(x) {
        if (this === x) {
            throw new Error('Chaining cycle detected for promise')
        } else if (x instanceof Promise2) {
            x.then(result => {
                this.#resolve(result)
            }, reason => {
                this.#reject(reason)
            })
        } else if (x instanceof Object) {
            const then = x.then
            if (typeof then === 'function') {
                try {
                    then(result => {
                        this.#resolve(result)
                    }, reason => {
                        this.#reject(reason)
                    })
                } catch (error) {
                    this.#reject(error)
                }
            } else {
                this.#resolve(x)
            }
        } else {
            this.#resolve(x)
        }
    }
}

// 微任务实现,可应用于浏览器和node环境
function nextTick(fn) {
    if (typeof window === 'undefined' && typeof process.nextTick === 'function') {
        return process.nextTick(fn)
    } else {
        const observer = new MutationObserver(fn)
        let counter = 1
        const textNode = document.createTextNode(String(counter))
        observer.observe(textNode, {
            characterData: true
        })
        counter += 2
        textNode.data = String(counter)
    }
}