Javascript异步编程核心概念-Promise原理剖析

96 阅读6分钟

Promise就是一个类,在实例化的时候传递一个函数作为执行器,并且会立即执行。这个执行器有两个参数:resolvereject,这两个参数实际上是一个函数。它们的目的就是为了更改Promise的状态。在Promise中有三种状态:等待pending、成功fulfilled、失败rejected,一旦状态确定就不可更改。pending --> fulfilled 、 pending --> rejected 、fulfilled -/-> rejectedresolvereject函数是用来更改状态的,resolve: fulfilled、reject:rejectedresolve接受一个成功的返回值,reject 接受一个失败的原因。

const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败
class Promise {
    constructor (executor) {
        executor(this.resolve, this.reject) // 立即执行执行器
    }
    status = PENDING // Promise状态
    resolve = () => { // 将Promise状态改为成功
        if (this.status !== PENDING) return // 当非PENDING状态时返回
        this.status = FULFILLED
    }
    reject = () => { // 将Promise状态改为失败
        if (this.status !== PENDING) return // 当非PENDING状态时返回
        this.status = REJECTED
    }
}

then方法做的事情就是判断状态,如果状态成功则调用成功回调函数;反之,调用失败回调函数。then方法是被定义在原型对象上的。then的成功回调函数 onFulfilled(value) value表示成功之后的值、onRejected(reason) reason 是失败的原因。

class Promise {
    ....
    value = undefined // 成功之后的值
    reason = undefined // 失败之后的原因
    resolve = value => {
        ...
        this.value = value // 保存成功之后的值
    }
    rejected = reason => {
        ...
        this.reason = reason // 保存失败之后的值
    }
    then (onFulfilled, onRejected) {
        /** 判断Promise状态 */
        if (this.status === FULFILLED) { // 成功状态
            onFulfilled(this.value)
        } else if (this.status === REJECTED) { // 失败状态
            onRejected(this.reason)
        }
    }
}

Promise内部处理异步函数的流程是:在then函数被调用执行时,判断Promise的状态,当状态还是pending时就把成功回调函数和失败回调函数缓存起来。并在resolvereject函数体内部判断是否有成功回调函数或者失败回调函数的缓存,有则执行。

class Promise {
    ...
    fulfilledCallback = undefined // 实例属性成功回调
    rejectedCallback = undefined // 实例属性失败回调
    resolve = value => {
        ....
        this.fulfilledCallback
            && this.fulfilledCallback(value) // 判断回调是否存在,存在就调用
    }
    reject = reason => {
        ...
        this.fulfilledCallback // 判断回调是否存在,存在就调用
            && this.rejectedCallback(reason)
    }
    then (onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            ...
        } else if (this.status === REJECTED) {
            ...
        } else { // 异步延迟执行下的等待状态
            this.fulfilledCallback = onFulfilled
            this.rejectedCallback = onRejected
        }
    }
}

在同一个Promise对象下的then方法是可以多次被调用的,这就需要将异步情况下成功回调函数或者失败回调函数缓存在数组中,并在resolvereject函数体中按序弹出 while(this.successCallback.length) this.successCallback.shift()(this.value)

class Promise {
    ...
    fulfilledCallbacks = [] // 实例属性成功回调
    rejectedCallbacks = [] // 实例属性失败回调
    resolve = value => {
        ....
        while(this.fulfilledCallbacks.length) // 判断回调是否存在,存在就调用
            this.fulfilledCallbacks.shift()(value)
    }
    reject = reason => {
        ...
        while(this.fulfilledCallbacks.length) // 判断回调是否存在,存在就调用
            this.rejectedCallbacks.shift()(reason)
    }
    then (onFulfilled, onRejected) {
        if (this.status === FULFILLED) {
            ...
        } else if (this.status === REJECTED) {
            ...
        } else { // 异步延迟执行下的等待状态
            this.fulfilledCallbacks.push(onFulfilled)
            this.rejectedCallbacks.push(onRejected)
        }
    }
}

then方法是可以被链式调用的,并且后面的then方法的回调函数拿到的值是这个then方法回调函数的返回值。要实现then方法的链式调用就是在then方法中return一个新的Promise实例,并将then的执行体传入新的Promise中作为执行器。这样就可以通过将本次成功回调函数的执行返回值传给新执行器的 resolve 来实现将上一个then回调函数的返回值传给下一个then的目的。

class Promise {
    ...
    then (onFulfilled, onRejected) {
        return new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                const result = onFulfilled(this.value)
                resolve(result)
            } else if (this.status === REJECTED) {
                ...
            } else {
                ...
            }
        })
    }
}

如果在上一个then的回调中又返回了一个Promise对象,我们在处理then函数时要对返回值进行类型的判断。

class Promise {
    ...
    then (onFulfilled, onRejected) {
        return new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                const result = onFulfilled(this.value)
                resolvePromise(result, resolve, reject)
            } else if (this.status === REJECTED) {
                ...
            } else {
                ...
            }
        })
    }
}
function resolvePromise (result, resolve, reject) {
    if (result instanceof Promise) {
        result.then(resolve, rejiect)
    } else {
        resolve(result)
    }
}

如果在then的回调中又返回了自身的这个Promise的实例就会发生循环调用的错误,为了避免这中错误,要在then的链式调用中进行识别判断该promise对象是否是自返回。

class Promise {
    ...
    then (...) {
        const promise = new Promise((...) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    ....
                    resolve(promise, result, resolve, reject)
                }, 0)
            } else if (this.status === REJECTED) {
                ...
            } else {
                ...
            }
        })
        return promise
    }
}
function resolvePromise (promise, result, resolve, reject) {
    if (promise === result)
        return reject(
            new TypeError('Chaining cycle detected for promise #<Promise>')
        )
    ...
}

错误捕获要考虑两个情况,第一个是在执行体内部发生错误,当执行器当中的代码在执行的过程中。发生错误的时候,就让Promise的状态变成失败状态。也就是在then方法的第二个参数要捕获到这个错误,可以在构造函数中执行器的外部添加 try catch

class Promise {
    constructor (executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }
    ...
}

第二个是在then方法的回调函数内部发生错误,这个错误要在下一个then方法执行时捕获到。这个捕获的实现是在then方法体中successCallbackfailCallback调用的外部添加try catch这里也要考虑异步的情况。

class Promise {
    resovle = value => {
        ...
        this.onFulfilledCallback.shift()()
    }
    reject = reason => {
        ...
        this.onRejectedCallback.shift()()
    }
    then (onFulfilled, onRejected) {
        const promise = new Promise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(_ => {
                    try {
                        const result = onFulfilled(this.value)
                        resolvePromise(promise, result, resovle, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else if (this.status ==== REJECTED) {
                setTimeout(_ => {
                    try {
                        const result = onRejected(this.reason)
                        resolvePromise(promise, result, resovle, reject)
                    } catch (error) {
                        reject(error)
                    }
                }, 0)
            } else {
                this.fulfilledCallbacks.push(_ => {
                    setTimeout(_ => {
                        try {
                            const result = onFulfilled(this.value)
                            resolvePromise(promise, result, resovle, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })                this.rejectedCallbacks.push(_ => {
                    setTimeout(_ => {
                        try {
                            const result = onRejected(this.reason)
                            resolvePromise(promise, result, resovle, reject)
                        } catch (error) {
                            reject(error)
                        }
                    }, 0)
                })            }
        })
        return promise
    }
}
...

then方法的参数变成可选参数,其实就是在没有参数的情况下做缺省处理:successCallback ? successCallback : value => value failCallback ? failCallback : reason => throw reason

class Promise {
    ...
    then (onFulfilled, onRejected) {
        onFulfilled ? onFulfilled : value => value
        onRejected ? onRejected : reason => { throw reason }
        ...
    }
    ...
}
...

Promise.all 方法是用来解决异步并发问题的,它允许我们按照异步代码调用的顺序得到异步代码执行的结果。它接受一个数组作为参数,数组中值的顺序一定是执行后得到的结果的顺序。它的返回值也是一个Promise对象,所以可以在它后面进行链式调用then方法。在all方法当中所有的promise对象如果它的状态都是成功的,all方法最后的状态也是成功的。如果又一个失败了,最终的结果它就是失败的。如果数组中的值是普通值,就可以放进结果数组当中,如果是一个Promise对象则直接先去执行这个Promise对象,然后在把Promise对象的结果放到结果数组当中去。

class Promise {
    ...
    static all (array) {
        let result = []
        let index = 0
        return Promise((resolve, reject) => {
            function addData (key, value) {
                result[key] = value
                index++
                if (index === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i]
                if (current instanceof Promise) {
                    current.then(
                        value => {
                            addData(i, value)                        },
                        reason => {
                            reject(reason)
                `       }
                    ) 
                } else {
                    addData(i, current)                }
            }
        })
        resolve(result)
    }
}

Promise.resolve 方法的的作用是将给定的值转换成Promise对象,就是它的返回值是一个promise对象。在这个返回的Promise对象当中去包裹这个给定的值,然后可以通过then方法拿到这个值。同时它也可以接收一个Promise对象,在它的内部可以判断传递的值是一个普通的值还是一个Promise对象。如果是Promise对象的话,它会把这个对象在作为这个resolve的返回值进行返回。

class Promise {
    ...
    static resolve (value) {
        if (value instanceof Promise) return value
        return new Promise(resolve => resolve(value))
    }
}

Promise.finally  有两个特点:1、无论这个Promise对象最终的状态是成功的还是失败的,finally方法当中的回调函数始终都会被执行;2、在finally方法的后面可以链式调用then方法来拿到当前这个Promise对象最终返回的结果;3、finally不是静态方法,它是定义在原型上的实例方法。

class Promise {
    ...
    finally (callback) {
        return this.then(value => {
            return Promise.resolve(callback()).then(_ => value)
        }, reason => {
            return Promise.resolve(callback()).then(_ => {throw reason})
        })
    }
}
...

Promise.catch 的作用是用来处理当前这个Promise对象最终的状态为失败的情况。

class Promise {
    ...
    catch (onRejected) {
        return this.then(undefined, onRejected)
    }
}
...