Promise - (四)Promise其他方法实现

本文详细介绍了如何重写Promise构造函数、then、catch、finally、resolve、reject以及Promise.all和Promise.race的方法,通过es6class形式展示,并探讨了这些方法在异步编程中的实际作用和优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

Promise构造函数,Promise.prototype.then方法重写

Promise.prototype.catch方法重写

Promise.prototype.finally方法重写

Promise.resolve函数重写

Promise.reject函数重写

Promise.all函数重写

Promise.race函数重写


(5条消息) 随笔-对Promise的深入理解_qfc_128220的博客-CSDN博客

前面完成了Promise构造函数和Promise.prototype.then方法的重写

但是还有如下方法没有重写

Promise.prototype.catch

Promise.prototype.finally

Promise.resolve

Promise.reject

Promise.all

Promise.race

这里我们先用es6 class语法将之前重写Promise构造函数和Promise.prototype.then再次重写一下

Promise构造函数,Promise.prototype.then方法重写

class Promise{
    constructor(excutor) {
        this.promiseState = 'pending'
        this.promiseResult = null
        // 异步回调需要被缓存到调用then的promise对象中,等待promise对象状态改变后执行,由于同一个promise对象可以分别调用多次then,而多次then指定的都是异步回调,所以需要缓存它们
        this.callbacks = []
        
        // 这里写成箭头函数,就不需要改变函数this指向了
        const resolve = (data) => {
            if(this.promiseState !== 'pending') return
            this.promiseState = 'fulfilled'
            this.promiseResult = data
            if(this.callbacks.length !== 0) {
                this.callbacks.forEach(item => {
                    item.onResolved(data)
                })
            }
        }

        const reject = (err) => {
            if(this.promiseState !== 'pending') return
            this.promiseState = 'rejected'
            this.promiseResult = err 
            if(this.callbacks.length !== 0) {
                this.callbacks.forEach(item => {
                    item.onRejected(err)
                })
            }
        }

        try {
            excutor(resolve, reject)
        } catch(e) {
            reject(e)
        }
    }


    then(onResolved, onRejected){
        // then方法可以不指定成功回调,此时需要生成默认成功回调,负责数据传递
        if(typeof onResolved !== 'function') {
            onResolved = data => data
        }

        // then方法可以不指定失败回调,此时需要生成默认失败回调,负责异常穿透
        if(typeof onRejected !== 'function') {
            onRejected = err => { throw err }
        }

        // then方法的返回值是一个新的promise对象
        return new Promise((resolve, reject) => {
            // 提取设置then方法返回的新promise对象的状态和结果的方法
            function common(onType, onTypeParam){
                try {
                    const result = onType(onTypeParam)

                    // 如果then指定回调函数的返回值是promise对象,则then方法返回值promise对象的结果和状态与 then方法指定回调函数返回值promise对象的结果和状态一致
                    if(result instanceof Promise) {
                        result.then(data => {
                            resolve(data)
                        }, err => {
                            reject(err)
                        })
                    } else {
                    // 如果then指定回调函数的返回值是非promise对象,则then方法返回值promise对象状态一定为成功,且结果为then指定回调函数的返回值
                        resolve(result)
                    }
                } catch(e) {
                    // 如果then指定回调函数执行过程中抛出异常,则then方法返回值promise对象为失败,且结果为异常信息
                    reject(e)
                }
            }

            // 注意下面的this都是then方法调用者promise对象的this,而不是then方法返回值promise对象的this
            // 调用then的promise对象的状态在调用then之前就已经确定为非pending,则为同步回调,对应的回调函数可以立即推入异步微任务队列(这里无法实现推入异步微任务队列,所以直接回调函数执行执行)
            if(this.promiseState === 'fulfilled') {
                common(onResolved, this.promiseResult)
            } else if (this.promiseState === 'rejected') {
                common(onRejected, this.promiseResult)
            } else {
            // 用then的promise对象的状态在调用then之后外还是pending的,则会异步回调,此时指定的回调函数需要被缓存在调用then的promise对象中,等待promise对象状态改变后再调用
                this.callbacks.push({
                   // 由于此时指定回调是异步调用的,所以对then方法返回值promise对象的状态和结果设置也需要在异步中进行,而不同在同步代码中设置,所以只能将缓存异步回调和调用异步回调封装在一起(放到一个函数中)
                    onResolved: function(data){
                        common(onResolved, data)
                    },
                    onRejected: function(err) {
                        common(onRejected, err)
                    }
                })
            }
        })
    }
}

Promise.prototype.catch方法重写

class Promise {
    constructor(excutor){ //... }

    then(onResolved, onRejected){ //... }

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

catch方法是对then方法功能的一种分担,我们直到then方法既可以指定成功回调,也可以指定失败回调。如果调用then的promise对象是失败状态,则会执行then指定的失败回调,如果调用then的promise对象是成功状态,则会执行then指定的成功回调。

但是如果在then链式调用中,我们为每一个then都指定成功回调和失败回调,则显得非常臃肿。为了优化链式调用中then的书写方式,Promise做了两件事:

1、then的默认失败回调,即如果then方法没有指定失败回调,则then方法内部会产生一个默认的失败回调,它的实现是 onRejected = err => { throw err }, 这个默认失败回调的作用是:如果调用then的promise对象是一个失败态的,且then没有指定失败回调,则最终then方法返回一个新的失败态的promise对象,且该promise对象的失败原因和调用者promise对象的失败原因相同。如果then链式中都没有指定失败回调,则会产生一个现象:异常穿透,即第一个then返回的是一个和调用者状态相同(失败态),结果相同的新promise对象,第二个then返回的是和第一个then返回promise对象状态相同,结果相同的新promise对象,这样就能将失败状态和失败原因一致传递到then链式的最后一个then的返回值promise对象

2、catch函数捕获异常,上面then使用默认失败回调会导致异常穿透现象,即then链式最后一个then返回的是一个失败态的,失败原因和初始then调用者promise对象相同的新的promise对象,此时如果不对链式返回的失败态promise对象进行失败回调的话,原生Promise则会抛出异常,所以Promise提供了catch方法来处理失败态的promise对象,而catch函数只接受一个失败回调作为参数,catch方法一般放在then链式尾部来捕获失败原因。

Promise.prototype.finally方法重写

class Promise {
    constructor(excutor) { //... }

    then(onResolved, onRejected) { //... }

    catch(onRejected) { //... }

    finally(onFinally) {
        return this.then(onFinally, onFinally)
    }
}

 如果将then类比为try,catch类比catch,finally类比为finally,则会发现,它们其实是功能对应的。

我们只在then中指定成功回调,在catch中指定失败回调,在finally中指定收尾处理。

比如then中需要查询数据库数据,但是查询异常了,被catch捕获到异常处理了,如果只做到这里,则会造成内存泄漏,因为我们必须要将建立的数据库连接释放,在没有finally时,我们需要对catch返回的promise对象,再做一次then(onFinally, onFinally),即无论成功还是失败都指向一样的回调,但是这样书写不好看,所以将其封装为finally方法

Promise.resolve函数重写

class Promise {
    constructor(excutor) { //... }

    then(onResolved, onRejected) { //... }

    catch(onRejected) { //... }

    finally(onFinally) { //... }

    static resolve(data) {
        return value instanceof Promise ? value : new Promise(resolve => resolve(value))
    }
}

resolve是Promise类的静态方法,可以快速地创建一个新的promise对象,而不需要繁琐地new Promise

另外resolve返回的promise对象的状态取决于入参,如果入参是非Promise类型数据,则resolve返回值一定是成功态的,且结果就是入参数据,如果入参是Promise类型数据,则resolve直接将入参作为返回值返回

Promise.reject函数重写

class Promise {
    constructor(excutor) { //... }

    then(onResolved, onRejected) { //... }

    catch(onRejected) { //... }

    finally(onFinally) { //... }

    static resolve(data) { //... }

    static reject(data) {
        return new Promise((resolve, reject) => {
            reject(data)
        })
    }
}

Promise.reject也是Promise类的静态方法,也是快速返回一个新的promise对象,但是和Promise.resolve不同的是,Promise.reject不管入参是否为Promise类型还是非Promise类型,Promise.reject的返回值一定是一个失败态的promise,且结果就是入参。需要注意的是,即使入参是一个promise对象,也会被封装进Promise.reject返回值promise对象的结果中。

Promise.all函数重写

class Promise {
    constructor(excutor) { //... }

    then(onResolved, onRejected) { //... }

    catch(onRejected) { //... }

    finally(onFinally) { //... }

    static resolve(data) { //... }

    static reject(data) { //... }

    static all(arr) {
        return new Promise((resolve, reject) => {
            let results = []
            let count = arr.length
            arr.forEach(item => {
                item.then(data => {
                    results.push(data)
                    if(--count === 0) {
                        resolve(results)
                    }
                }).catch(err => {
                    reject(err)
                })
            })
        })
    }
}

Promise.all应用场景:我们需要等待多个异步任务的结果,只有所有异步任务的结果都到齐了才能继续后续处理。如果多个异步任务中有一个失败,则整体失败。

Promise.all接收一个数组,数组元素全部为promise对象。Promise.all返回一个新的promise对象,该promise对象的状态和结果取决于Promise.all入参的数组的每一个元素

如果入参数组的所有元素都是成功态的promise,则Promise.all返回的promise对象就是成功态的,且其结果为一个数组,数组元素是Promise.all入参数组的每一个promise对象元素的结果

如果入参数组的某一个元素是失败态的promise,则Promise.all返回的promise对象就是失败态的,且其结果为入参数组失败态promise对象的结果

这里Promise.all的重写需要考虑异步,即如果入参数组元素promise对象的封装的是异步任务,则Promise.all实现需要考虑异步

Promise.race函数重写

class Promise {
    constructor(excutor) { //... }

    then(onResolved, onRejected) { //... }

    catch(onRejected) { //... }

    finally(onFinally) { //... }

    static resolve(data) { //... }

    static reject(data) { //... }

    static all(arr) { //... }

    static race(arr) {
        return new Promise((resolve, reject) => {
            arr.forEach(item => {
                item.then(data => {
                    resolve(data)
                }).catch(err => {
                    reject(err)
                })
            })
        })
    }
}

Promise.race应用场景:开启多个异步任务做同一件事,谁先完成,就使用谁的结果。如果最快的那么是失败的,则整体失败。

Promise.race也是Promise类的静态方法,接收一个数组,数组元素都是promise对象。

Promise.race返回值是一个新的promise对象,它的状态和结果取决于Promise.race入参的数组中一个完成状态改变的promise对象元素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员阿甘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值