From 85f80e94e4dc69c84d3dc407b6763cbb39daf5a0 Mon Sep 17 00:00:00 2001 From: homura Date: Wed, 29 Nov 2023 17:49:01 +0800 Subject: [PATCH] fix(utils): stop immediately when retry limited (#576) --- packages/utils/src/async/index.ts | 49 +++++++++++++++++++++--------- packages/utils/tests/async.test.ts | 27 +++++++++++++--- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/packages/utils/src/async/index.ts b/packages/utils/src/async/index.ts index 9e86f6aa6..493f25b5e 100644 --- a/packages/utils/src/async/index.ts +++ b/packages/utils/src/async/index.ts @@ -43,13 +43,22 @@ export function timeout( typeof options === "number" ? options : options.milliseconds ?? 1000; const message = typeof options === "number" ? undefined : options.message; - const timeoutPromise = delay(milliseconds).then(() => - Promise.reject( - message instanceof Error ? message : createTimeoutError(message) - ) - ); + return new Promise((resolve, reject) => { + const timeoutTask = setTimeout(() => { + reject(message instanceof Error ? message : createTimeoutError(message)); + }, milliseconds); - return race([promise, timeoutPromise]); + promise.then( + (value) => { + clearTimeout(timeoutTask); + resolve(value); + }, + (error) => { + clearTimeout(timeoutTask); + reject(error); + } + ); + }); } export interface RetryOptions { @@ -73,20 +82,30 @@ export function retry( delay: delayMs = 0, } = options; - let lastErr: unknown; - let times = 0; + let currentRetryTimes = 0; const retryPromise = new Promise((resolve, reject) => { - function retryRun() { - times++; - if (times > retries) { - reject(lastErr); + function handleError(err: unknown) { + if (currentRetryTimes > retries) { + reject(err); return; } - Promise.resolve(run()).then(resolve, (e) => { - lastErr = e; + + currentRetryTimes++; + + if (delayMs) { delay(delayMs).then(retryRun); - }); + } else { + retryRun(); + } + } + + function retryRun() { + try { + Promise.resolve(run()).then(resolve, handleError); + } catch (err) { + handleError(err); + } } retryRun(); diff --git a/packages/utils/tests/async.test.ts b/packages/utils/tests/async.test.ts index bf400f632..6e263a373 100644 --- a/packages/utils/tests/async.test.ts +++ b/packages/utils/tests/async.test.ts @@ -32,12 +32,14 @@ test("async#timeout", async (t) => { test("async#retry", async (t) => { function passOn(times: number): () => Promise { - let count = 0; + let runTimes = 0; return () => new Promise((resolve, reject) => { - count++; - if (count >= times) resolve(); - else reject(new Error("Failed")); + if (runTimes > times) { + return resolve(); + } + runTimes++; + reject(new Error("Failed")); }); } @@ -71,3 +73,20 @@ test("async#retry with timeout", async (t) => { t.true(called); t.true(failed); }); + +test("async#retry with delay", async (t) => { + const before = Date.now(); + + await t.throwsAsync(() => + retry( + () => { + throw new Error("delay"); + }, + { retries: 5, delay: 50, timeout: 1000 } + ) + ); + + const after = Date.now(); + + t.true(after - before >= 50 * 5); +});