# `Moar.Retry`
[🔗](https://github.com/synchronal/moar/blob/main/lib/retry.ex#L1)

Retryable functions.

These functions are particularly useful in higher-level tests, though they can be used for any
kind of waiting.

See also the [retry](https://hex.pm/packages/retry) Hex package. 

One use of `rescue_for/3` in a test is to wait until an assertion succeeds:

```elixir
# wait up to 5 seconds for the element with ID "status" to be "finished"
Moar.Retry.rescue_for!(5000, fn ->
  assert view |> element("#status") |> render() == "finished"
end)
```

One use of `retry_for/3` in a test is to wait until something changes before making an assertion:

```elixir
Moar.Retry.retry_for(5000, fn ->
  view |> element("#status") |> render() == "finished"
end)

assert view |> element("#total") |> render() == "8,675,309"
```

# `rescue_for!`

```elixir
@spec rescue_for!(pos_integer() | Moar.Duration.t(), (-&gt; any()), pos_integer()) ::
  any() | no_return()
```

Run `fun` every `interval_ms` until it doesn't raise an exception.
If `timeout` expires, the exception will be re-raised.

* `timeout` can be an integer in milliseconds or a `Moar.Duration` tuple

```elixir
iex> Moar.Retry.rescue_for!(20, fn -> raise "always fails" end, 2)
** (RuntimeError) always fails

iex> Moar.Retry.rescue_for!({20, :millisecond}, fn -> raise "always fails" end, 2)
** (RuntimeError) always fails
```

# `rescue_until!`

```elixir
@spec rescue_until!(DateTime.t(), (-&gt; any()), pos_integer()) :: any() | no_return()
```

Run `fun` every `interval_ms` until it doesn't raise an exception.
If the current time reaches `expiry`, the exception will be re-raised.

```elixir
iex> date_time = DateTime.add(DateTime.utc_now(), 20, :millisecond)
iex> Moar.Retry.rescue_until!(date_time, fn -> raise "always fails" end, 2)
** (RuntimeError) always fails
```

# `retry_for`

```elixir
@spec retry_for(pos_integer() | Moar.Duration.t(), fun(), pos_integer()) ::
  {:ok, any()} | {:error, :timeout}
```

Run `fun` every `interval_ms` until it returns a truthy value, returning `{:ok, <value>}`.
If `timeout` expires, returns `{:error, :timeout}`.

* `timeout` can be an integer in milliseconds or a `Moar.Duration` tuple

```elixir
iex> Moar.Retry.retry_for(20, fn -> 10 end, 2)
{:ok, 10}

iex> Moar.Retry.retry_for(20, fn -> false end, 2)
{:error, :timeout}
```

# `retry_until`

```elixir
@spec retry_until(DateTime.t(), fun(), pos_integer()) ::
  {:ok, any()} | {:error, :timeout}
```

Run `fun` every `interval_ms` until it returns a truthy value, returning `{:ok, <value>}`.
If the current time reaches `expiry`, returns `{:error, :timeout}`.

```elixir
iex> date_time = DateTime.add(DateTime.utc_now(), 20, :millisecond)
iex> Moar.Retry.retry_until(date_time, fn -> false end, 2)
{:error, :timeout}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
