# Racing and Timeouts

Racing runs several effects concurrently and keeps the **first** result,
interrupting the rest. It's the building block behind timeouts, hedged requests,
and "whichever replica answers first" patterns. Because Effect's
[interruption](https://effect.plants.sh/concurrency/interruption/) is safe, the losers are cancelled
cleanly — their finalizers run, their resources are released.

```ts
import { Effect } from "effect"

// Two replicas of the same query; we only want the faster answer.
const replicaA = Effect.succeed("from A").pipe(Effect.delay("200 millis"))
const replicaB = Effect.succeed("from B").pipe(Effect.delay("100 millis"))

const program = Effect.gen(function*() {
  // Run both concurrently; the first to *succeed* wins and the loser is
  // interrupted. Here B is faster, so the result is "from B".
  const result = yield* Effect.race(replicaA, replicaB)
  yield* Effect.log(result)
})

Effect.runFork(program)
```

## `race` — first to succeed wins

`Effect.race(a, b)` returns the result of whichever effect **succeeds** first and
interrupts the other. A failure does *not* win the race: if one effect fails,
`race` waits for the other. Only if *both* fail does the race fail, with a
combined cause holding both errors.

```ts
import { Effect } from "effect"

const fails = Effect.fail("a failed").pipe(Effect.delay("100 millis"))
const wins = Effect.succeed("b").pipe(Effect.delay("200 millis"))

// `a` fails first, but failure doesn't win — so the result is "b".
const program = Effect.race(fails, wins)
```

If you want the *first to settle* regardless of success or failure, see
`raceFirst` below, or wrap each side in `Effect.result` to turn its failure into
a value that can win the race.

## `raceAll` — race a collection

`Effect.raceAll` is the n-ary form: it takes an iterable of effects, returns the
first success, and interrupts the rest. If every effect fails, it fails with a
combined cause holding all of their errors.

```ts
import { Effect } from "effect"

const mirror = (name: string, ms: number) =>
  Effect.succeed(name).pipe(Effect.delay(`${ms} millis`))

const program = Effect.gen(function*() {
  // Hit three mirrors at once; take whichever responds first.
  const fastest = yield* Effect.raceAll([
    mirror("us-east", 150),
    mirror("eu-west", 90),
    mirror("ap-south", 220)
  ])
  yield* Effect.log(fastest) // "eu-west"
})

Effect.runFork(program)
```

## `raceFirst` — first to *settle* wins

Unlike `race`, `Effect.raceFirst(a, b)` returns the result of whichever effect
**completes first, whether it succeeds or fails**. Use it when the first
*outcome* is what you care about, not the first success.

```ts
import { Effect } from "effect"

const failFast = Effect.fail("nope").pipe(Effect.delay("100 millis"))
const succeedSlow = Effect.succeed("ok").pipe(Effect.delay("200 millis"))

// `failFast` settles first, so the whole race fails with "nope".
const program = Effect.raceFirst(failFast, succeedSlow)
```

## Timeouts

A timeout is just a race against a timer, and `Effect.timeout` packages it up.
After the duration elapses, the underlying effect is interrupted (running its
finalizers) and the timeout case is surfaced.

```ts
import { Effect } from "effect"

const slowCall = Effect.succeed("response").pipe(Effect.delay("5 seconds"))

const program = Effect.gen(function*() {
  // Fail with a TimeoutError if `slowCall` doesn't finish within 2 seconds.
  // The call is interrupted when the timeout fires.
  const response = yield* slowCall.pipe(Effect.timeout("2 seconds"))
  yield* Effect.log(response)
})

Effect.runFork(program)
```

`Effect.timeout` adds a `Cause.TimeoutError` to the effect's error channel. You
then handle it like any other typed error (see
[Error Management](https://effect.plants.sh/error-management/)):

```ts
import { Cause, Effect } from "effect"

const program = Effect.succeed("response").pipe(
  Effect.delay("5 seconds"),
  Effect.timeout("2 seconds"),
  // Recover from the timeout specifically.
  Effect.catchTag("TimeoutError", () => Effect.succeed("fallback response"))
)
```

### Timeout variants

Choose the variant that matches how you want the timeout case represented:

| API | Timeout becomes | Use when |
| --- | --- | --- |
| `Effect.timeout(d)` | a typed `TimeoutError` failure | the timeout is an error you'll handle or report |
| `Effect.timeoutOption(d)` | success with `Option.none()` | a timeout means "no result", not a failure |
| `Effect.timeoutOrElse({ duration, orElse })` | the result of a fallback effect | you have a sensible default or alternative to run |

```ts
import { Effect, Option } from "effect"

const slow = Effect.succeed("value").pipe(Effect.delay("5 seconds"))

// `timeoutOption`: absence instead of failure.
const asOption = slow.pipe(Effect.timeoutOption("1 second"))
//    ^? Effect<Option<string>, never>

// `timeoutOrElse`: run a fallback when time runs out.
const withFallback = slow.pipe(
  Effect.timeoutOrElse({
    duration: "1 second",
    orElse: () => Effect.succeed("default value")
  })
)
```
**Tip:** Combine timeouts with [Scheduling](https://effect.plants.sh/scheduling/) for robust calls: wrap a
  flaky operation in `Effect.timeout`, then `Effect.retry` it with a backoff
  schedule so each attempt is bounded *and* transient failures are retried.

## A note on the losers

In every operator here, the effects that don't win are **interrupted**, not left
running in the background. That interruption is back-pressured by default: the
race resumes only once the losers have finished cleaning up, which keeps
resource usage predictable. If a loser's cleanup is itself slow and you don't
want to wait on it, you can detach it — but for the common case, the default
behavior is exactly what you want.