Closed lionel-rowe closed 4 days ago
CI failing on unrelated unstable_spinner
test:
Spinner.color can set each color => ./cli/unstable_spinner_test.ts:142:6
error: AssertionError: Expected actual: "
⠋
" to contain: "
⠙ ".
throw new AssertionError(msg);
^
at assertStringIncludes (file:///D:/a/std/std/assert/string_includes.ts:29:9)
at file:///D:/a/std/std/cli/unstable_spinner_test.ts:146:3
FAILURES
Spinner.color can set each color => ./cli/unstable_spinner_test.ts:142:6
Because we'd like to accept every new feature as unstable feature (ref), can you copy the existing retry.ts
/retry_test.ts
to unstable_retry.ts
/unstable_retry_test.ts
and move these changes to there?
Because we'd like to accept every new feature as unstable feature (ref), can you copy the existing
retry.ts
/retry_test.ts
tounstable_retry.ts
/unstable_retry_test.ts
and move these changes to there?
Done. Current output for git diff --no-index async/retry.ts async/unstable_retry.ts
:
diff --git a/async/retry.ts b/async/unstable_retry.ts
index d2f0250c..0dc80c86 100644
--- a/async/retry.ts
+++ b/async/unstable_retry.ts
@@ -2,33 +2,13 @@
// This module is browser compatible.
import { exponentialBackoffWithJitter } from "./_util.ts";
+import { RetryError } from "./retry.ts";
/**
- * Error thrown in {@linkcode retry} once the maximum number of failed attempts
- * has been reached.
+ * Options for {@linkcode retry}.
*
- * @example Usage
- * ```ts no-assert ignore
- * import { RetryError } from "@std/async/retry";
- *
- * throw new RetryError({ foo: "bar" }, 3);
- * ```
+ * @experimental **UNSTABLE**: New API, yet to be vetted.
*/
-export class RetryError extends Error {
- /**
- * Constructs a new {@linkcode RetryError} instance.
- *
- * @param cause the cause for this error.
- * @param attempts the number of retry attempts made.
- */
- constructor(cause: unknown, attempts: number) {
- super(`Retrying exceeded the maxAttempts (${attempts}).`);
- this.name = "RetryError";
- this.cause = cause;
- }
-}
-
-/** Options for {@linkcode retry}. */
export interface RetryOptions {
/**
* How much to backoff after each retry.
@@ -61,6 +41,16 @@ export interface RetryOptions {
* @default {1}
*/
jitter?: number;
+
+ /**
+ * Callback to determine if an error or other thrown value is retriable.
+ *
+ * @default {() => true}
+ *
+ * @param err The thrown error or other value.
+ * @returns `true` if the error is retriable, `false` otherwise.
+ */
+ isRetriable?: (err: unknown) => boolean;
}
/**
@@ -68,6 +58,8 @@ export interface RetryOptions {
* Retries as long as the given function throws. If the attempts are exhausted,
* throws a {@linkcode RetryError} with `cause` set to the inner exception.
*
+ * @experimental **UNSTABLE**: New API, yet to be vetted.
+ *
* The backoff is calculated by multiplying `minTimeout` with `multiplier` to the power of the current attempt counter (starting at 0 up to `maxAttempts - 1`). It is capped at `maxTimeout` however.
* How long the actual delay is, depends on `jitter`.
*
@@ -127,6 +119,7 @@ export async function retry<T>(
maxAttempts = 5,
minTimeout = 1000,
jitter = 1,
+ isRetriable = () => true,
} = options ?? {};
if (maxTimeout <= 0) {
@@ -150,6 +143,10 @@ export async function retry<T>(
try {
return await fn();
} catch (error) {
+ if (!isRetriable(error)) {
+ throw error;
+ }
+
if (attempt + 1 >= maxAttempts) {
throw new RetryError(error, maxAttempts);
}
Closes #6196