大前端与勇士

打工人带你一起刷大前端副本 111

使用promise封装一个retry

今天被问到:使用promise封装一个retry函数,如果一个请求失败,重试几次后也失败就抛出错误

以下为失败尝试,可以直接跳转到完整案例

如果您有更好的案例,欢迎分享

完整案例

失败经历

我简单思考了一下,这里可以用递归,写出了如下代码

function retry (task, count) {
    return new Promise((resolve, reject) => {
        task.then(res => { // 未执行
            resolve(res)
        })
        .catch(() => {
            if (count > 0) {
                return retry(task, count--) // 死循环
            }
            else {
                reject('次数用完了')
            }
        })
    })
}

写了一个测试用例,好家伙,直接崩了。。。
修改注释的错误后,我又上路了

function retry (task, count) {
    return new Promise((resolve, reject) => {
        task().then(res => {
            resolve(res)
        })
        .catch(() => {
            if (count > 0) {
                return retry(task, count-1)
            }
            else {
                reject('次数用完了')
            }
        })
    })
}

这次算是能实现失败后重试了,但是,当次数用完后抛出了一个错误,程序不能继续执行了

可是我明明使用了.catch捕获reject啊,这就令人费解了
报错嘛,大不了我try、catch一下

那么try、catch加在哪里呢?我想到使用async、await时的一种场景

async function run() {
    try {
        await retry(task, 1)
    }
    catch (e) {
        console.log(e)
    }
    console.log('done')
}
 run() 

我低估了异步的错误,这家伙成精了啊
于是我开始搜索如何解决promise的错误
都是清一色的.catch(e => {}),我也是这么写的啊,怎么会没有用呢
我想问题应该在于我catch到了第一次调用,第一次失败并没有reject,而是递归调用了自身,生成了一个新的promise对象
于是我换了一个思路

function retry(task, count) {
    console.log('还有' + count + '次')
    return new Promise((resolve, reject) => {
        task()
            .then(res => {
                resolve(res)
            })
            .catch(() => {
                if (count > 0) {
                    retry(task, count - 1)
                } else {
                    reject('次数用完了')
                }
            })
    }).catch(e => console.log(e)) // 捕获当前promise的错误
}

这次确实成功捕获到了错误,但是我没法拿到错误,也没能继续执行下去
既然确定了是因为重试生成promise对象和之前的没有关联,那么,问题很简单就解决了

完整案例

经过几次失败,这个案例应该趋于成功了吧

function retry(task, count) {
    console.log('还有' + count + '次')
    return new Promise((resolve, reject) => {
        task()
            .then(res => {
                resolve(res)
            })
            .catch(() => {
                if (count > 0) {
                    resolve(retry(task, count - 1)) // 这里使用resolve
                } else {
                    reject('次数用完了')
                }
            })
    })
}

posted on 2020-12-21 16:48  秦伟杰  阅读(540)  评论(0编辑  收藏  举报

导航