# Retry

`retry` wraps an async function and re-runs it on failure.

## retry

Wraps a `() => Promise<T>` with retry logic. Retries up to `attempts` times on failure. The optional `delay` is inserted **between** failures only — not after the final one. Each attempt runs in an effect scope, so `onCleanup` inside the function fires before each retry.

```ts
import { retry } from "elements-kit/utilities/retry";

const fn = retry(
  () => fetch("/api/data").then((r) => r.json()),
  3,           // up to 3 attempts
  (n) => n * 500, // 0ms, 500ms, 1000ms between failures
);

await fn(); // retries automatically on failure
```

```ts
function retry<T>(
  fn: () => Promise<T>,
  attempts: number,
  delay?: number | ((attempt: number) => number),
): () => Promise<T>
```

Compose with `async()` for reactive retries:

```ts
import { async } from "elements-kit/utilities/async";
import { retry } from "elements-kit/utilities/retry";
import { onCleanup } from "elements-kit/signals";

const fetchTodo = async((id: number) =>
  retry(() => {
    const controller = new AbortController();
    onCleanup(() => controller.abort()); // aborts before each retry
    return fetch(`/api/todos/${id}`, { signal: controller.signal }).then((r) => r.json());
  }, 3, 500)(),
).start();
```

***

## See also

* [Async](/primitives/async)
* [Data fetching example](/examples/data-fetching)
