如何重试 Promise 解决方案 N 次,两次尝试之间有延迟? [英] How to retry a Promise resolution N times, with a delay between the attempts?

查看:49
本文介绍了如何重试 Promise 解决方案 N 次,两次尝试之间有延迟?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一些 JavaScript 代码将 3 个东西作为参数:

I want some JavaScript code to take 3 things as parameters:

  • 返回 Promise 的函数.
  • 最大尝试次数.
  • 每次尝试之间的延迟.

我最终做的是使用 for 循环.我不想使用递归函数:这样,即使有 50 次尝试,调用堆栈也不会长 50 行.

What I ended up doing is using a for loop. I did not want to use a recursive function : this way, even if there are 50 attempts the call stack isn't 50 lines longer.

这是代码的打字稿版本:

/**
 * @async
 * @function tryNTimes<T> Tries to resolve a {@link Promise<T>} N times, with a delay between each attempt.
 * @param {Object} options Options for the attempts.
 * @param {() => Promise<T>} options.toTry The {@link Promise<T>} to try to resolve.
 * @param {number} [options.times=5] The maximum number of attempts (must be greater than 0).
 * @param {number} [options.interval=1] The interval of time between each attempt in seconds.
 * @returns {Promise<T>} The resolution of the {@link Promise<T>}.
 */
export async function tryNTimes<T>(
    {
        toTry,
        times = 5,
        interval = 1,
    }:
        {
            toTry: () => Promise<T>,
            times?: number,
            interval?: number,
        }
): Promise<T> {
    if (times < 1) throw new Error(`Bad argument: 'times' must be greater than 0, but ${times} was received.`);
    let attemptCount: number;
    for (attemptCount = 1; attemptCount <= times; attemptCount++) {
        let error: boolean = false;
        const result = await toTry().catch((reason) => {
            error = true;
            return reason;
        });

        if (error) {
            if (attemptCount < times) await delay(interval);
            else return Promise.reject(result);
        }
        else return result;
    }
}

上面使用的delay函数是一个promisified timeout:

The delay function used above is a promisified timeout:

/**
 * @function delay Delays the execution of an action.
 * @param {number} time The time to wait in seconds.
 * @returns {Promise<void>}
 */
export function delay(time: number): Promise<void> {
    return new Promise<void>((resolve) => setTimeout(resolve, time * 1000));
}

澄清:上面的代码有效,我只是想知道这是否是一种好"的做法,如果不是,我该如何改进.

To clarify: the code above works, I'm only wondering if this is a "good" way of doing it, and if not, how I could improve it.

有什么建议吗?预先感谢您的帮助.

Any suggestion? Thanks in advance for your help.

推荐答案

我不想使用递归函数:这样,即使有 50 次尝试,调用堆栈也不会长 50 行.

I did not want to use a recursive function: this way, even if there are 50 attempts the call stack isn't 50 lines longer.

这不是一个好借口.调用堆栈不会因异步调用而溢出,当递归解决方案比迭代解决方案更直观时,您可能应该采用它.

That's not a good excuse. The call stack doesn't overflow from asynchronous calls, and when a recursive solution is more intuitive than an iterative one you should probably go for it.

我最终做的是使用 for 循环.这是一种好"的做法吗?如果不是,我该如何改进?

What I ended up doing is using a for loop. Is this a "good" way of doing it, and if not, how I could improve it?

for 循环很好.虽然它从 1 开始有点奇怪,但基于 0 的循环更加惯用.

The for loop is fine. It's a bit weird that it starts at 1 though, 0-based loops are much more idiomatic.

然而,你奇怪的错误处理是不好的.该布尔 error 标志不应该出现在您的代码中.使用 .catch() 很好,但 try/catch 也能正常工作,应该是首选.

What is not fine however is your weird error handling. That boolean error flag should have no place in your code. Using .catch() is fine, but try/catch would work just as well and should be preferred.

export async function tryNTimes<T>({ toTry, times = 5, interval = 1}) {
    if (times < 1) throw new Error(`Bad argument: 'times' must be greater than 0, but ${times} was received.`);
    let attemptCount = 0
    while (true) {
        try {
            const result = await toTry();
            return result;
        } catch(error) {
            if (++attemptCount >= times) throw error;
        }
        await delay(interval)
    }
}

这篇关于如何重试 Promise 解决方案 N 次,两次尝试之间有延迟?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆