实现Promise

174 阅读2分钟

实现 Promise 需要完全读懂 Promise A+ 规范,不过从总体的实现上看,有如下几个点需要考虑到:

  • then 需要支持链式调用,所以得返回一个新的 Promise;
  • 处理异步问题,所以得先用 onResolvedCallbacks 和 onRejectedCallbacks 分别把成功和失败的回调存起来;
  • 为了让链式调用正常进行下去,需要判断 onFulfilled 和 onRejected 的类型;
  • onFulfilled 和 onRejected 需要被异步调用,这里用 setTimeout 模拟异步;
  • 处理 Promise 的 resolve;
class MyPromise {
    constructor(executor) {
        this.status = 'pending'
        this.result = undefined
        this.reason = undefined
        this.onResolvedArr = []
        this.onRejectedArr = []
        const resolve = (result) => {
            if (this.status === 'pending') {
                this.status = 'resolved'
                this.result = result
                this.onResolvedArr.map(fn => fn())
            }
        }
        const reject = (reason) => {
            if (this.status === 'pending') {
                this.status = 'rejected'
                this.reason = reason
                this.onRejectedArr.map(fn => fn())
            }
        }
        try {
            executor(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
    then(onResolved, onRejected) {
        //判断参数类型,一定要为函数
        onResolved = typeof onResolved === 'function' ? onResolved : result => result
        onRejected = typeof onRejected === 'function' ? onRejected : err => {
            throw new Error(err)
        }
        //为了promise的链式调用,返回一个新的promise
        const newPromise = new MyPromise((resolve, reject) => {
            if (this.status === 'resolved') {
                setTimeout(() => {  //为了让promise的回调变成异步,加上计时器
                    try {
                        const result = onResolved(this.result)
                        // 为了继续调用resolve,reject 封装一个方法去调用,并且可以继续返回promise
                        handlePromise(result, newPromise, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }

                })
            }
            if (this.status === 'rejected') {
                setTimeout(() => {
                    try {
                        const result = onRejected(this.reason)
                        handlePromise(result, newPromise, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }

                })
            }
            //为了解决promise内存在异步代码,导致then执行时status状态未变更,导致无法执行,做一个发布订阅模式
            if (this.status === 'pending') {
                this.onResolvedArr.push(() => {
                    try {
                        const result = onResolved(this.result)
                        handlePromise(result, newPromise, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }
                })
                this.onRejectedArr.push(() => {
                    try {
                        const result = onRejected(this.reason)
                        handlePromise(result, newPromise, resolve, reject)
                    } catch (err) {
                        reject(err)
                    }

                })
            }
        })
        return newPromise
    }
    catch (onRejected) {
        return this.then(undefined, onRejected)
    }
}
const handlePromise = (result, newPromise, resolve, reject) => {
    if (result === newPromise) {
        throw new Error('can not return oneself')
    }
    if (typeof result === 'function' || (typeof result === 'object' && result !== null)) {
        let lock = false
        try {
            const then = result.then
            if (typeof then === 'function') {
                then.call(result, (r) => {
                    if (lock) return
                    handlePromise(r, newPromise, resolve, reject)
                    lock = true
                }, (e) => {
                    if (lock) return
                    reject(e)
                    lock = true
                })
            } else {
                resolve(result)
            }
        } catch (err) {
            reject(err)
        }
    } else {
        resolve(result)
    }
}
const test = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('哈哈哈')
    }, 1000)

})
test.then((res) => {
    console.log(res)
    return 11111
}, (err) => {
    console.log(err)
}).then(res => {
    console.log(res)
})
console.log(111111111111)