如何重试 Promise 解决 N 次,尝试之间有延迟?
Posted
技术标签:
【中文标题】如何重试 Promise 解决 N 次,尝试之间有延迟?【英文标题】:How to retry a Promise resolution N times, with a delay between the attempts? 【发布时间】:2019-08-31 22:19:49 【问题描述】:我想要一些 javascript 代码将 3 个东西作为参数:
返回 Promise 的函数。 最大尝试次数。 每次尝试之间的延迟。我最终做的是使用for
循环。我不想使用递归函数:这样,即使有 50 次尝试,调用堆栈也不会长 50 行。
这里是 typescript 版本的代码:
/**
* @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
函数是一个承诺超时:
/**
* @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));
澄清一下:上面的代码有效,我只是想知道这是否是一种“好”的做法,如果不是,我该如何改进它。
有什么建议吗?提前感谢您的帮助。
【问题讨论】:
由于代码可以正常工作并且应该没有问题,这可能是codereview.stackexchange.com的问题 这个我不知道,谢谢。 Promise Retry Design Patterns的可能重复 【参考方案1】:你考虑过 RxJS 吗?
它非常适合在异步工作流中实现这种逻辑。
下面是一个示例,说明如何在不破坏公共 api 的情况下执行此操作(即从 Promise 转换为 Observable 并返回)。在实践中,您可能希望在任何给定项目中使用 RxJS 或 Promises,而不是混合使用它们。
/**
* @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;
return from(toTry)
.pipe(
retryWhen(errors =>
errors.pipe(
delay(interval * 1000),
take(times - 1)
)
)
)
.toPromise();
可能不值得为这一逻辑添加一个完整的库,但如果您的项目涉及很多复杂的异步工作流程,那么 RxJS 就很棒。
【讨论】:
这不是有点像用大炮射麻雀吗?【参考方案2】:我不想使用递归函数:这样,即使有 50 次尝试,调用堆栈也不会长 50 行。
这不是一个好的借口。调用堆栈不会因异步调用而溢出,当递归解决方案比迭代解决方案更直观时,您可能应该选择它。
我最终做的是使用
for
循环。这是一种“好”的做法吗?如果不是,我该如何改进它?
for
循环很好。虽然它从 1
开始有点奇怪,但基于 0 的循环更加惯用。
然而,你奇怪的错误处理并不好。该布尔值error
标志不应在您的代码中出现。 Using .catch()
is fine,但 try
/catch
也可以,应该是首选。
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)
【讨论】:
【参考方案3】:您可能想看看async-retry,它正是您所需要的。这个包允许你重试异步操作,你可以配置(除其他外)重试之间的超时(即使是增加的因素)、最大重试次数……
这样您就不必重新发明***,而是可以依赖在社区中广泛使用的经过验证的软件包。
【讨论】:
【参考方案4】:使用带有 Promise 的递归函数不会成为调用堆栈的问题,因为 Promise 会立即返回,并且将在异步事件之后调用 then
或 catch
函数。
一个简单的javascript函数如下:
function wait (ms)
return new Promise((resolve) =>
setTimeout(resolve, ms)
)
function retry (fn, maxAttempts = 1, delay = 0, attempts = 0)
return Promise.resolve()
.then(fn)
.catch(err =>
if (attempts < maxAttempts)
return retry (fn, maxAttempts, delay, attempts + 1)
throw err
)
【讨论】:
以上是关于如何重试 Promise 解决 N 次,尝试之间有延迟?的主要内容,如果未能解决你的问题,请参考以下文章