diff --git a/CHANGELOG.md b/CHANGELOG.md index 631ecdc..602a547 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ # Changelog +## v7.0.0 + +- break `retryDelay` function signature to add error that caused retry and an + abort controller to cancel retrying. + ## v6.2.2 -- update `retryDelay` signature for more consistency with other stuff like interceptors. +- update `retryDelay` signature for more consistency with other stuff like + interceptors. ## v6.2.1 diff --git a/README.md b/README.md index 6526248..2c33e85 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ This library offers a fetch wrapper that can: - accept a `timeout` option and abort when timeout is reached - accept a `retry` option and retry the request when it throws - accept a `retryDelay` option to wait before retrying. it can be a function. + further retries can be cancelled as well! - add `Accept` header with value `application/json, text/plain, */*` if not already set by you - set global headers when creating the wrapper @@ -21,7 +22,7 @@ This library offers a fetch wrapper that can: you can import `wrapFetch` from `mod.ts` file. ```ts -export { wrapFetch } from "https://deno.land/x/fetch_goody@v6.2.2/mod.ts"; +export { wrapFetch } from "https://deno.land/x/fetch_goody@v7.0.0/mod.ts"; ``` ### wrapFetch diff --git a/extended_request_init.ts b/extended_request_init.ts index 9815144..217ba70 100644 --- a/extended_request_init.ts +++ b/extended_request_init.ts @@ -18,9 +18,15 @@ interface RequestInitDiff { } export type RetryDelayFunction = ( - /** current attempt (1 = it is going to retry for the first time and so on) */ - attempt: number, - init: ExtendedRequest, + options: { + /** current attempt (1 = it is going to retry for the first time and so on) */ + attempt: number; + request: ExtendedRequest; + /** the error causing retry */ + error: unknown; + /** calling this AbortController's abort will stop retrying the request */ + abortController: AbortController; + }, ) => number; export type Interceptors = { diff --git a/fetch_wrapper.ts b/fetch_wrapper.ts index 4249aad..17efc0f 100644 --- a/fetch_wrapper.ts +++ b/fetch_wrapper.ts @@ -288,9 +288,16 @@ export function wrapFetch(options?: WrapFetchOptions) { retryDelay, ); if (typeof delayVal === "function") { + const retryAbortController = new AbortController(); await utils.delay( - delayVal(attempt, interceptedInit as ExtendedRequest), + delayVal({ + attempt, + request: interceptedInit as ExtendedRequest, + error: e, + abortController: retryAbortController, + }), ); + retryAbortController.signal.throwIfAborted(); } else { await utils.delay(delayVal); } diff --git a/fetch_wrapper_test.ts b/fetch_wrapper_test.ts index 295649c..d35fe67 100644 --- a/fetch_wrapper_test.ts +++ b/fetch_wrapper_test.ts @@ -765,9 +765,9 @@ Deno.test("Retry option", { }, }, retry: 3, - retryDelay: (attempt, init) => { + retryDelay: ({ attempt, request }) => { lastAttempt = attempt; - lastInit = init; + lastInit = request; assertStrictEquals(count, attempt); return 0; }, @@ -797,4 +797,45 @@ Deno.test("Retry option", { ); }, ); + + await t.step( + "retryDelay can be aborted", + async () => { + const wrappedFetch = wrapFetch(); + + let count = 0; + + try { + await wrappedFetch(serverOneUrl + "/count", { + body: "foo", + interceptors: { + request() { + count++; + throw new Error("arbitrary"); + }, + }, + retry: 10, + retryDelay: ({ abortController }) => { + if (count >= 2) { + // means if we already failed 2 times, do not retry anymore + abortController.abort(); + } + return 0; + }, + }); + } catch { + } + + assert( + count < 10, + "count (" + count + ") is bigger than retry (10)", + ); + + assertStrictEquals( + count, + 2, + "retry count is " + count + " but should be " + 2, + ); + }, + ); });