深入浅出Promise

145 阅读3分钟

promise的状态

const PromiseStatus = {
    PENDING: "pending",
    FULFILLED: "fulfilled",
    REJECTED: "rejected"
}

image.png

如上图所示, promise的几个状态之间的关系,接下来我们将一步步实现一个自己的CustomPromise

开始构建promise对象

class CustomPromise {
    status = PromiseStatus.PENDING
    data = null
    error = null
    
    constructor(executor) {
        const resolve = (data) => {}
        const reject = (error) => {}
        executor(resolve, reject)
    }
}

如上代码所示, 当我们new Promise()时到到底是如何执行的,

推进promise的状态

1. reject时

const rejectPromise = (promise, reason) => {
    if (promise.status !== PromiseStatus.PENDING) {
        return
    }
    promise.status = PromiseStatus.REJECTED
    promise.error = reason
    promise._flushHandlers()
}

class CustomPromise {
    status = PromiseStatus.PENDING
    data = null
    error = null
    
    constructor(executor) {
        const resolve = (data) => { }

        const reject = (error) => {
            rejectPromise(this, error)
        }
        executor(resolve, reject)
    }

}

如上所示, 我们在reject后将状态推进为rejected,并记录错误信息error

挂载then方法

众所周知, promise的实例是可以连续then的,然后后面then回调函数的入参是由前面的then回调函数的返回值决定的, 是有链式影响效果的, 所以调用then方法后是返回一个promise实例的, 以用于后面继续then或者catch

 then(successCallback, errorCallback) {
    const promise = new CustomPromise(() => { })
    this.#onFulfilledCallbacks.push({
        successCallback,
        errorCallback,
        promise
    })
    return promise
}

在这段代码, 我记录了then所挂载的新任务, 以方便resolve之后执行

调用promise的then方法所挂载的任务

const isFunction = (value) => value && typeof value === "function"
    
const resolvePromise = (promise, info) => {
    if (promise.status !== PromiseStatus.PENDING) {
        return
    }
    // 判断info是不是和当前实例对象相同
    // console.log("判断info是不是和当前实例对象相同", info === this);

    if (promise === info) {
        const error = new TypeError("Chaining cycle detected for promise #<CustomPromise>")
        rejectPromise(promise, error)
        throw error;
    }

    // 判断对象是不是一个thenable对象
    if (!promise.isThenable(info)) {
        promise.status = PromiseStatus.FULFILLED
        promise.data = info
        promise._flushHandlers()
        return
    }

    // 当前info是一个thenable对象, 当前实例需要吸收info的状态
    queueMicrotask(() => {
        // 吸收对象的过程是在微队列进行的
        info.then((res) => {
            resolvePromise(promise, res)
        }, (err) => {
            rejectPromise(promise, err)
        })
    })
}

_flushHandlers() {
    if (this.status === PromiseStatus.PENDING) {
        return
    }
    queueMicrotask(() => {
        while (this.#onFulfilledCallbacks.length) {
            const { 
                successCallback, 
                errorCallback,
                promise 
            } = this.#onFulfilledCallbacks.shift();
            if (!isFunction(successCallback) && this.status === PromiseStatus.FULFILLED) {
                resolvePromise(promise, this.data)
                continue;
            }
            if (!isFunction(errorCallback) && this.status === PromiseStatus.REJECTED) {
                console.log('task', promise);
                rejectPromise(promise, this.status)
                continue;
            }
            if(this.status === PromiseStatus.FULFILLED){
                resolvePromise(promise, successCallback(this.data))
                continue;
            }
            if(this.status === PromiseStatus.REJECTED){
                rejectPromise(promise, errorCallback(this.error))
                continue;
            }
        }
    })
}

首先, 如果当前实例状态并未更新到完成状态(fulfilled和rejected统称为完成状态), 说明未到回调任务之时, 其次, 若是then方法中所传参数不是函数, 则更新返回的实例的状态和参数即可

更新完所有状态后, 我们的promise实例也算是走完了整个流程, 下面奉上所有源码
const PromiseStatus = {
    PENDING: "pending",
    FULFILLED: "fulfilled",
    REJECTED: "rejected"
}

const isFunction = (value) => value && typeof value === "function"

const rejectPromise = (promise, reason) => {
    if (promise.status !== PromiseStatus.PENDING) {
        return
    }
    promise.status = PromiseStatus.REJECTED
    promise.error = reason
    promise._flushHandlers()
}

const resolvePromise = (promise, info) => {
    if (promise.status !== PromiseStatus.PENDING) {
        return
    }
    // 判断info是不是和当前实例对象相同
    // console.log("判断info是不是和当前实例对象相同", info === this);

    if (promise === info) {
        const error = new TypeError("Chaining cycle detected for promise #<CustomPromise>")
        rejectPromise(promise, error)
        throw error;
    }

    // 判断对象是不是一个thenable对象
    if (!promise.isThenable(info)) {
        promise.status = PromiseStatus.FULFILLED
        promise.data = info
        promise._flushHandlers()
        return
    }

    // 当前info是一个thenable对象, 当前实例需要吸收info的状态
    queueMicrotask(() => {
        // 吸收对象的过程是在微队列进行的
        info.then((res) => {
            resolvePromise(promise, res)
        }, (err) => {
            rejectPromise(promise, err)
        })
    })
}


class CustomPromise {
    status = PromiseStatus.PENDING
    data = null
    error = null
    #onFulfilledCallbacks = []

    _flushHandlers() {
        if (this.status === PromiseStatus.PENDING) {
            return
        }
        queueMicrotask(() => {
            while (this.#onFulfilledCallbacks.length) {
                const { 
                    successCallback, 
                    errorCallback, 
                    promise 
                } = this.#onFulfilledCallbacks.shift();
                if (!isFunction(successCallback) && this.status === PromiseStatus.FULFILLED) {
                    resolvePromise(promise, this.data)
                    continue;
                }
                if (!isFunction(errorCallback) && this.status === PromiseStatus.REJECTED) {
                    console.log('task', promise);
                    rejectPromise(promise, this.status)
                    continue;
                }
                if(this.status === PromiseStatus.FULFILLED){
                    resolvePromise(promise, successCallback(this.data))
                    continue;
                }
                if(this.status === PromiseStatus.REJECTED){
                    rejectPromise(promise, errorCallback(this.error))
                    continue;
                }
            }
        })
    }

    constructor(executor) {
        try {
            const resolve = (data) => {
                resolvePromise(this, data)
            }

            const reject = (error) => {
                rejectPromise(this, error)
            }
            executor(resolve, reject)
        } catch (error) {
            rejectPromise(this, error)
        }
    }

    isThenable(fun) {
        return fun && (typeof fun === 'function' 
        || typeof fun === 'object') && typeof fun.then === 'function'
    }

    then(successCallback, errorCallback) {
        const promise = new CustomPromise(() => { })
        this.#onFulfilledCallbacks.push({
            successCallback,
            errorCallback,
            promise
        })
        this._flushHandlers()
        return promise
    }

    catch(errorCallback) {
        return this.then(null, errorCallback)
    }
}