diff --git a/docs/ja/reference/function/retry.md b/docs/ja/reference/function/retry.md index 8d9ec5de3..1c3f490de 100644 --- a/docs/ja/reference/function/retry.md +++ b/docs/ja/reference/function/retry.md @@ -50,6 +50,22 @@ const data4 = await retry( delay: attempts => Math.min(100 * Math.pow(2, attempts), 5000), } ); + +// エラーに基づく再試行間隔 (例: Retry-After ヘッダー) +const data5 = await retry( + async () => { + return await fetchData(); + }, + { + retries: 3, + delay: (_attempts, error) => { + if ((error as { status?: number }).status === 429) { + return (error as { retryAfter: number }).retryAfter * 1000; + } + return 1000; + }, + } +); ``` 特定のエラーでのみ再試行したい場合は `shouldRetry` オプションを使用できます。 @@ -64,7 +80,7 @@ class NetworkError extends Error { } // 500エラー以上でのみ再試行 -const data5 = await retry( +const data6 = await retry( async () => { const response = await fetch('/api/data'); if (!response.ok) { @@ -111,7 +127,7 @@ try { - `func` (`() => Promise`): 再試行する非同期関数です。 - `options` (`number | RetryOptions`, オプション): 再試行回数またはオプションオブジェクトです。 - `retries` (`number`, オプション): 再試行する回数です。デフォルトは `Infinity` で無限に再試行します。 - - `delay` (`number | (attempts: number) => number`, オプション): 再試行間隔(ミリ秒)です。数値または関数を使用できます。デフォルトは `0` です。 + - `delay` (`number | (attempts: number, error: unknown) => number`, オプション): 再試行間隔(ミリ秒)です。数値または関数を使用できます。関数は試行回数とエラーオブジェクトを受け取ります。デフォルトは `0` です。 - `signal` (`AbortSignal`, オプション): 再試行をキャンセルできるシグナルです。 - `shouldRetry` (`(error: unknown, attempt: number) => boolean`, オプション): 再試行するかどうかを決定する関数です。`false` を返すと即座にエラーをスローします。 - `error`: 発生したエラーオブジェクトです。 diff --git a/docs/ko/reference/function/retry.md b/docs/ko/reference/function/retry.md index 397aba13e..ed90b7142 100644 --- a/docs/ko/reference/function/retry.md +++ b/docs/ko/reference/function/retry.md @@ -50,6 +50,22 @@ const data4 = await retry( delay: attempts => Math.min(100 * Math.pow(2, attempts), 5000), } ); + +// 에러 기반 재시도 간격 (예: Retry-After 헤더 활용) +const data5 = await retry( + async () => { + return await fetchData(); + }, + { + retries: 3, + delay: (_attempts, error) => { + if ((error as { status?: number }).status === 429) { + return (error as { retryAfter: number }).retryAfter * 1000; + } + return 1000; + }, + } +); ``` 특정 에러에서만 재시도하고 싶을 때 `shouldRetry` 옵션을 사용할 수 있어요. @@ -64,7 +80,7 @@ class NetworkError extends Error { } // 500 에러 이상에서만 재시도 -const data5 = await retry( +const data6 = await retry( async () => { const response = await fetch('/api/data'); if (!response.ok) { @@ -111,7 +127,7 @@ try { - `func` (`() => Promise`): 재시도할 비동기 함수예요. - `options` (`number | RetryOptions`, 선택): 재시도 횟수나 옵션 객체예요. - `retries` (`number`, 선택): 재시도할 횟수예요. 기본값은 `Infinity`로 무한 재시도해요. - - `delay` (`number | (attempts: number) => number`, 선택): 재시도 간격(밀리초)이에요. 숫자나 함수를 사용할 수 있어요. 기본값은 `0`이에요. + - `delay` (`number | (attempts: number, error: unknown) => number`, 선택): 재시도 간격(밀리초)이에요. 숫자나 함수를 사용할 수 있어요. 함수는 시도 횟수와 에러 객체를 인자로 받아요. 기본값은 `0`이에요. - `signal` (`AbortSignal`, 선택): 재시도를 취소할 수 있는 시그널이에요. - `shouldRetry` (`(error: unknown, attempt: number) => boolean`, 선택): 재시도 여부를 결정하는 함수예요. `false`를 반환하면 즉시 에러를 던져요. - `error`: 발생한 에러 객체예요. diff --git a/docs/reference/function/retry.md b/docs/reference/function/retry.md index b5c606450..110768a0d 100644 --- a/docs/reference/function/retry.md +++ b/docs/reference/function/retry.md @@ -50,6 +50,22 @@ const data4 = await retry( delay: attempts => Math.min(100 * Math.pow(2, attempts), 5000), } ); + +// Error-based retry interval (e.g., respect Retry-After headers) +const data5 = await retry( + async () => { + return await fetchData(); + }, + { + retries: 3, + delay: (_attempts, error) => { + if ((error as { status?: number }).status === 429) { + return (error as { retryAfter: number }).retryAfter * 1000; + } + return 1000; + }, + } +); ``` You can use `shouldRetry` option when you want to retry only on specific errors. @@ -64,7 +80,7 @@ class NetworkError extends Error { } // Retry only on 500+ errors -const data5 = await retry( +const data6 = await retry( async () => { const response = await fetch('/api/data'); if (!response.ok) { @@ -111,7 +127,7 @@ try { - `func` (`() => Promise`): The asynchronous function to retry. - `options` (`number | RetryOptions`, optional): The number of retries or options object. - `retries` (`number`, optional): The number of times to retry. Defaults to `Infinity` for infinite retries. - - `delay` (`number | (attempts: number) => number`, optional): The retry interval (in milliseconds). Can be a number or a function. Defaults to `0`. + - `delay` (`number | (attempts: number, error: unknown) => number`, optional): The retry interval (in milliseconds). Can be a number or a function. The function receives the attempt number and the error object. Defaults to `0`. - `signal` (`AbortSignal`, optional): A signal that can cancel retries. - `shouldRetry` (`(error: unknown, attempt: number) => boolean`, optional): A function that determines whether to retry. If it returns `false`, the error is thrown immediately. - `error`: The error object that occurred. diff --git a/docs/zh_hans/reference/function/retry.md b/docs/zh_hans/reference/function/retry.md index c8c910054..bb1d310fd 100644 --- a/docs/zh_hans/reference/function/retry.md +++ b/docs/zh_hans/reference/function/retry.md @@ -50,6 +50,22 @@ const data4 = await retry( delay: attempts => Math.min(100 * Math.pow(2, attempts), 5000), } ); + +// 基于错误的重试间隔 (如: 使用 Retry-After 响应头) +const data5 = await retry( + async () => { + return await fetchData(); + }, + { + retries: 3, + delay: (_attempts, error) => { + if ((error as { status?: number }).status === 429) { + return (error as { retryAfter: number }).retryAfter * 1000; + } + return 1000; + }, + } +); ``` 当只想在特定错误时重试时,可以使用 `shouldRetry` 选项。 @@ -64,7 +80,7 @@ class NetworkError extends Error { } // 仅在 500+ 错误时重试 -const data5 = await retry( +const data6 = await retry( async () => { const response = await fetch('/api/data'); if (!response.ok) { @@ -111,7 +127,7 @@ try { - `func` (`() => Promise`): 要重试的异步函数。 - `options` (`number | RetryOptions`, 可选): 重试次数或选项对象。 - `retries` (`number`, 可选): 重试次数。默认值为 `Infinity`,无限重试。 - - `delay` (`number | (attempts: number) => number`, 可选): 重试间隔(毫秒)。可以使用数字或函数。默认值为 `0`。 + - `delay` (`number | (attempts: number, error: unknown) => number`, 可选): 重试间隔(毫秒)。可以使用数字或函数。函数接收尝试次数和错误对象。默认值为 `0`。 - `signal` (`AbortSignal`, 可选): 可以取消重试的信号。 - `shouldRetry` (`(error: unknown, attempt: number) => boolean`, 可选): 决定是否重试的函数。如果返回 `false`,则立即抛出错误。 - `error`: 发生的错误对象。 diff --git a/src/function/retry.spec.ts b/src/function/retry.spec.ts index 8b8df87c1..0696da094 100644 --- a/src/function/retry.spec.ts +++ b/src/function/retry.spec.ts @@ -98,6 +98,16 @@ describe('retry', () => { expect(shouldRetry).toHaveBeenCalledWith(error, 0); }); + it('should pass error to delay function', async () => { + const error = new Error('Server Error'); + const func = vi.fn().mockRejectedValueOnce(error).mockResolvedValue('success'); + const delayFn = vi.fn(() => 0); + + await retry(func, { delay: delayFn, retries: 1 }); + + expect(delayFn).toHaveBeenCalledWith(0, error); + }); + it('should pass attempt number to shouldRetry', async () => { const error = new Error('failure'); const func = vi.fn().mockRejectedValue(error); diff --git a/src/function/retry.ts b/src/function/retry.ts index e8d7e49e8..f35829105 100644 --- a/src/function/retry.ts +++ b/src/function/retry.ts @@ -7,9 +7,9 @@ interface RetryOptions { * * @default 0 * @example - * delay: (attempts) => attempt * 50 + * delay: (attempts, error) => error.status === 429 ? 10000 : attempts * 50 */ - delay?: number | ((attempts: number) => number); + delay?: number | ((attempts: number, error: unknown) => number); /** * The number of retries to attempt. @@ -73,7 +73,7 @@ export async function retry(func: () => Promise, retries: number): Promise * @template T * @param func - The function to retry. It should return a promise. * @param options - Options to configure the retry behavior. - * @param [options.delay=0] - Delay(milliseconds) between retries. + * @param [options.delay=0] - Delay(milliseconds) between retries. A number or a function that receives the attempt number and the error object. * @param [options.retries=Infinity] - The number of retries to attempt. * @param [options.signal] - An AbortSignal to cancel the retry operation. * @param [options.shouldRetry] - A function that determines whether to retry. @@ -88,7 +88,10 @@ export async function retry(func: () => Promise, retries: number): Promise * retry(() => fetchData(), { delay: 1000, retries: 5 }); * * // Retry a function with a delay increasing linearly by 50ms per attempt - * retry(() => fetchData(), { delay: (attempts) => attempt * 50, retries: 5 }); + * retry(() => fetchData(), { delay: (attempts) => attempts * 50, retries: 5 }); + * + * // Retry a function with an error-aware delay (e.g., respect Retry-After headers) + * retry(() => fetchData(), { delay: (_attempts, error) => error.status === 429 ? 10000 : 1000, retries: 3 }); * * @example * // Retry a function with exponential backoff + jitter (max delay 10 seconds) @@ -108,7 +111,7 @@ export async function retry(func: () => Promise, options: RetryOptions): P * @returns A promise that resolves with the value of the successful function call. */ export async function retry(func: () => Promise, _options?: number | RetryOptions): Promise { - let delay: number | ((attempts: number) => number); + let delay: number | ((attempts: number, error: unknown) => number); let retries: number; let signal: AbortSignal | undefined; let shouldRetry: (error: unknown, attempt: number) => boolean; @@ -142,7 +145,7 @@ export async function retry(func: () => Promise, _options?: number | Retry throw err; } - const currentDelay = typeof delay === 'function' ? delay(attempts) : delay; + const currentDelay = typeof delay === 'function' ? delay(attempts, error) : delay; await delayToolkit(currentDelay); } }