# Fibers

A **fiber** is the running execution of an effect. Where an `Effect<A, E, R>` is
a lazy, immutable *description* of a computation, a `Fiber<A, E>` is a live
*handle* on one that has actually started. You get a fiber by **forking** an
effect; with that handle you can wait for the result, inspect how it finished,
or cancel it.

Notice that a `Fiber` has no `Requirements` type parameter — only success `A`
and error `E`. By the time an effect is running in a fiber, its requirements have
already been provided, so there is nothing left to inject.

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

// A small CPU-bound computation we can run in the background.
const fib = (n: number): Effect.Effect<number> =>
  n < 2
    ? Effect.succeed(n)
    : Effect.zipWith(fib(n - 1), fib(n - 2), (a, b) => a + b)

const program = Effect.gen(function*() {
  // `forkChild` starts `fib(10)` in a new fiber and hands us back the handle.
  // The fiber is a child of this one, so its lifetime is tied to ours.
  const fiber = yield* Effect.forkChild(fib(10))

  // ...do other work concurrently here...

  // `join` suspends until the fiber finishes and returns its value.
  // If the fiber had failed, that failure would be re-raised here.
  const result = yield* Fiber.join(fiber)
  yield* Effect.log(`fib(10) = ${result}`) // fib(10) = 55
})

Effect.runFork(program)
```

## Forking

`Effect.forkChild(effect)` returns an `Effect<Fiber<A, E>>` — running it starts a
new fiber and gives you its handle. By default the forked fiber begins executing
only after the current fiber yields (so don't assume it has made progress the
instant `forkChild` returns). Pass `{ startImmediately: true }` if you want it to
begin running right away.

The child is **automatically supervised**: it is bound to the fiber that forked
it. When the parent completes — normally, by failure, or by interruption — any
still-running children are interrupted and their finalizers run. This is what
"structured concurrency" means in practice, and it is the default you almost
always want.

## Joining vs. awaiting

There are two ways to wait for a fiber, and they differ in how they treat
failure:

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

const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(Effect.fail("boom" as const))

  // `Fiber.await` always succeeds with an Exit describing the outcome.
  // It never re-raises — use it to *inspect* success, failure, or interruption.
  const exit = yield* Fiber.await(fiber)
  yield* Effect.log(exit)
  // { _id: 'Exit', _tag: 'Failure', cause: { ... failure: 'boom' } }
})

Effect.runFork(program)
```

- **`Fiber.join`** waits and *re-raises* the fiber's failure into the joining
  fiber. Use it when the child's result is part of your happy path and a failure
  should propagate.
- **`Fiber.await`** waits and returns an [`Exit`](https://effect.plants.sh/data-types/) value instead —
  it never fails. Use it when you want to *handle* whatever happened (success,
  failure, or interruption) yourself.

You can wait on several fibers at once with `Fiber.joinAll` and
`Fiber.awaitAll`.

## Interrupting

When you no longer need a fiber's result, interrupt it. This immediately
signals the fiber to stop, runs all of its finalizers to release resources, and
completes once the fiber has terminated.

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

const program = Effect.gen(function*() {
  // A fiber that logs forever, every 200ms.
  const fiber = yield* Effect.forkChild(
    Effect.forever(Effect.log("tick").pipe(Effect.delay("200 millis")))
  )

  yield* Effect.sleep("500 millis")

  // Signal interruption and wait until the fiber has fully torn down.
  yield* Fiber.interrupt(fiber)
  yield* Effect.log("fiber stopped")
})

Effect.runFork(program)
```

`Fiber.interrupt` returns `Effect<void>` and, by default, **back-pressures**: it
does not resume until the target fiber has finished running its finalizers. That
guarantees no new work starts before the old work has cleaned up. If you'd
rather not wait, fork the interruption itself with
`Effect.forkChild(Fiber.interrupt(fiber))`.
**Note:** Interruption is **cooperative**. A fiber can keep running while it is inside
  an uninterruptible region or a finalizer, so `interrupt` may take a while to
  resume. See [Interruption](https://effect.plants.sh/concurrency/interruption/) for the full model.
**Tip:** A fiber can also cancel *itself* with `Effect.interrupt`, and you can run
  cleanup on cancellation with `Effect.onInterrupt`. Both are covered in
  [Interruption](https://effect.plants.sh/concurrency/interruption/).

## Child fiber lifetimes

`forkChild` is one of four fork strategies. They differ only in *what scope owns
the child* — that is, when the child gets interrupted:

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

const task = Effect.forever(Effect.log("working").pipe(Effect.delay("1 second")))

// 1. Supervised by the parent fiber (the default, structured choice).
//    Interrupted when the forking fiber ends.
const a = Effect.forkChild(task)

// 2. Bound to the surrounding Scope. The fiber can outlive the forking fiber
//    and is interrupted when the Scope closes. Adds `Scope` to requirements.
const b = Effect.forkScoped(task)

// 3. Bound to a *specific* Scope you captured earlier — finer-grained control.
const c = Effect.gen(function*() {
  const scope = yield* Effect.scope
  return yield* Effect.forkIn(task, scope)
})

// 4. Detached: attached to the global scope, runs as a daemon. Not interrupted
//    when the forking fiber ends — only when the runtime shuts down.
const d = Effect.forkDetach(task)
```

| Strategy | Tied to | Use when |
| --- | --- | --- |
| `forkChild` | the forking fiber | the default — the child is part of this unit of work |
| `forkScoped` | the surrounding `Scope` | the fiber should outlive the forking fiber but die with a [resource scope](https://effect.plants.sh/resource-management/) |
| `forkIn` | a specific captured `Scope` | you need to choose exactly which scope owns the fiber |
| `forkDetach` | the global scope (daemon) | a long-running background task that must survive its parent |

`forkScoped` is the typical choice for a background worker that a service owns
for as long as the service's layer is alive:

```ts
import { Context, Effect, Layer } from "effect"

// A service that runs a background heartbeat for its whole lifetime.
export class Heartbeat extends Context.Service<Heartbeat, {
  readonly tick: Effect.Effect<void>
}>()("app/Heartbeat") {
  static readonly layer = Layer.effect(
    Heartbeat,
    Effect.gen(function*() {
      // `forkScoped` ties the worker to the layer's Scope: when the layer is
      // released, the worker fiber is interrupted automatically — no manual
      // bookkeeping, no leaked background fiber.
      yield* Effect.forkScoped(
        Effect.forever(
          Effect.log("heartbeat").pipe(Effect.delay("5 seconds"))
        )
      )

      return Heartbeat.of({ tick: Effect.log("manual tick") })
    })
  )
}
```
**Caution:** Daemon fibers from `forkDetach` are *not* supervised by their parent, so an
  uncaught failure in one won't surface anywhere unless you observe it. Reach
  for `forkChild` or `forkScoped` first; use `forkDetach` only when you
  genuinely want a task to escape its parent's lifetime.

## When you need many fibers

Forking and joining a handful of fibers by hand is fine. Once you're forking one
per element of a collection, prefer the
[concurrency option](https://effect.plants.sh/concurrency/concurrency-options/) on `Effect.forEach` /
`Effect.all` — it forks, supervises, and bounds parallelism for you. And when
you need to keep a *dynamic* set of fibers around (e.g. one per connection),
reach for the [fiber collections](https://effect.plants.sh/concurrency/fiber-collections/):
`FiberHandle`, `FiberMap`, and `FiberSet`.

## Fork variants (`Effect`)

Forking is the entry point to fibers, so the fork operators live on `Effect`.
All four return `Effect<Fiber<A, E>, never, R>` and accept the same
`{ startImmediately?, uninterruptible? }` options; they differ only in which
scope owns the child.

### Effect.forkChild

Forks an effect into a child fiber supervised by the **current fiber**. When the
current fiber ends, the child is interrupted. This is the structured-concurrency
default.

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

const program = Effect.gen(function*() {
  // Fork without blocking; the child starts after this fiber next yields.
  const fiber = yield* Effect.forkChild(Effect.succeed(42))

  // Start immediately instead of waiting for the next yield point.
  const eager = yield* Effect.forkChild(Effect.succeed(1), {
    startImmediately: true
  })

  return yield* Fiber.join(fiber) // => 42
})

Effect.runFork(program)
```

### Effect.forkScoped

Forks into the **enclosing `Scope`** rather than the current fiber, so the child
can outlive the forking fiber and is interrupted when the scope closes. Adds
`Scope` to the effect's requirements.

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

const program = Effect.scoped(
  Effect.gen(function*() {
    // Lives until the surrounding `scoped` block (the Scope) closes.
    yield* Effect.forkScoped(
      Effect.forever(Effect.log("tick").pipe(Effect.delay("1 second")))
    )
    yield* Effect.sleep("2500 millis")
    // => logs "tick" ~twice, then the scope closes and the fiber is interrupted
  })
)

Effect.runFork(program)
```

### Effect.forkIn

Forks into a **specific `Scope`** you captured earlier — the same as
`forkScoped` but you choose exactly which scope owns the fiber.

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

const task = Effect.gen(function*() {
  yield* Effect.sleep("10 seconds")
  return "completed"
})

const program = Effect.scoped(
  Effect.gen(function*() {
    const scope = yield* Effect.scope
    const fiber = yield* Effect.forkIn(task, scope)
    yield* Effect.sleep("1 second")
    // `fiber` is interrupted when `scope` closes
    return fiber
  })
)

Effect.runFork(program)
```

### Effect.forkDetach

Forks into the **global scope** as a daemon. The child is *not* interrupted when
the forking fiber ends — only when the runtime shuts down. Use it for long-lived
background work that must escape its parent.

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

const daemon = Effect.forever(
  Effect.log("daemon running...").pipe(Effect.delay("1 second"))
)

const program = Effect.gen(function*() {
  yield* Effect.forkDetach(daemon)
  yield* Effect.log("daemon started")
  // The daemon keeps running after this fiber completes.
})

Effect.runFork(program)
```

### Effect.withFiber

Gives the surrounding effect access to the **current fiber** so you can read its
runtime fields (id, scheduler, references). The callback returns an effect.

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

const program = Effect.withFiber((fiber) =>
  Effect.succeed(`Fiber ID: ${fiber.id}`)
)

Effect.runPromise(program).then(console.log)
// => Fiber ID: 1
```

### Effect.fiber

The current fiber as a plain value (`Effect<Fiber<unknown, unknown>>`), for when
you want to `yield*` it inside `Effect.gen` rather than take a callback.

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

const program = Effect.gen(function*() {
  const self = yield* Effect.fiber
  yield* Effect.log(`running on fiber ${self.id}`) // => running on fiber 1
})

Effect.runFork(program)
```

### Effect.fiberId

The current fiber's numeric id (`Effect<number>`). Handy for tagging logs and
spans.

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

const program = Effect.gen(function*() {
  const id = yield* Effect.fiberId
  return id // => 1
})

Effect.runFork(program)
```

### Effect.awaitAllChildren

Delays the wrapped effect's completion until **all child fibers it forked** have
finished. Children that already existed before the wrapped effect started are
not awaited.

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

const program = Effect.gen(function*() {
  yield* Effect.forkChild(Effect.sleep("1 second").pipe(Effect.andThen(Effect.log("child done"))))
  yield* Effect.log("parent body done")
}).pipe(Effect.awaitAllChildren)
// => "parent body done", then ~1s later "child done", then the effect completes

Effect.runFork(program)
```

## Fiber module reference

The functions below operate on a `Fiber<A, E>` handle you already hold.

### Fiber.await

Waits for a fiber to complete and returns its [`Exit`](https://effect.plants.sh/data-types/) — it never
fails, so use it to *inspect* the outcome (success, failure, interruption).

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

const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(Effect.succeed(42))
  const exit = yield* Fiber.await(fiber)
  return exit // => Exit.succeed(42)
})

Effect.runFork(program)
```

### Fiber.join

Waits for a fiber and **propagates** its result into the current effect: success
returns the value, failure re-raises the fiber's `Cause`.

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

const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(Effect.succeed(42))
  const result = yield* Fiber.join(fiber)
  return result // => 42
})

Effect.runFork(program)
```

### Fiber.awaitAll

Waits for every fiber in an iterable and returns an array of their `Exit` values,
in input order. Failures are captured as `Exit.Failure` rather than re-raised.

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

const program = Effect.gen(function*() {
  const f1 = yield* Effect.forkChild(Effect.succeed(1))
  const f2 = yield* Effect.forkChild(Effect.succeed(2))
  const exits = yield* Fiber.awaitAll([f1, f2])
  return exits // => [Exit.succeed(1), Exit.succeed(2)]
})

Effect.runFork(program)
```

### Fiber.joinAll

Waits for every fiber to **succeed** and returns their values in input order. The
first failure fails the returned effect and stops waiting — but it does **not**
interrupt the remaining fibers (use `Fiber.interruptAll` for that).

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

const program = Effect.gen(function*() {
  const f1 = yield* Effect.forkChild(Effect.succeed("a"))
  const f2 = yield* Effect.forkChild(Effect.succeed("b"))
  const values = yield* Fiber.joinAll([f1, f2])
  return values // => ["a", "b"]
})

Effect.runFork(program)
```

### Fiber.interrupt

Requests cancellation of a fiber and resumes only after it has finished running
its finalizers. Interruption is cooperative.

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

const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(
    Effect.delay("1 second")(Effect.succeed(42))
  )
  yield* Fiber.interrupt(fiber)
  return "interrupted" // => "interrupted"
})

Effect.runFork(program)
```

### Fiber.interruptAs

Like `interrupt`, but records a specific **interruptor fiber id** in the cause —
useful for diagnostics and tracing. It does not change interruption semantics.

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

const program = Effect.gen(function*() {
  const target = yield* Effect.forkChild(
    Effect.delay("5 seconds")(Effect.succeed("task"))
  )
  yield* Fiber.interruptAs(target, 123) // attributed to fiber #123
  return "done"
})

Effect.runFork(program)
```

### Fiber.interruptAll

Interrupts every fiber in an iterable, recording the current fiber as the
interruptor. Resumes only after all of them have finished.

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

const program = Effect.gen(function*() {
  const f1 = yield* Effect.forkChild(Effect.sleep("5 seconds"))
  const f2 = yield* Effect.forkChild(Effect.sleep("3 seconds"))
  yield* Fiber.interruptAll([f1, f2])
  return "all interrupted" // => "all interrupted"
})

Effect.runFork(program)
```

### Fiber.interruptAllAs

Like `interruptAll`, but attributes the interruption to a specific fiber id.

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

const program = Effect.gen(function*() {
  const controller = yield* Effect.forkChild(Effect.succeed("controller"))
  const worker = yield* Effect.forkChild(Effect.sleep("5 seconds"))
  yield* Fiber.interruptAllAs([worker], controller.id)
  return "interrupted by controller"
})

Effect.runFork(program)
```

### Fiber.isFiber

Type guard that checks whether an unknown value is a `Fiber`.

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

const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(Effect.succeed(42))
  return [Fiber.isFiber(fiber), Fiber.isFiber("hello")] // => [true, false]
})

Effect.runFork(program)
```

### Fiber.getCurrent

Synchronous accessor (not an effect) that returns the currently executing fiber,
or `undefined` outside a fiber runtime context. For low-level runtime
integrations; prefer `Effect.withFiber` / `Effect.fiber` in normal code.

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

const program = Effect.sync(() => {
  const current = Fiber.getCurrent()
  return current?.id // => 1 (inside a fiber)
})

Effect.runFork(program)
```

### Fiber.runIn

Registers a manually managed fiber with a `Scope` so it is interrupted when the
scope closes, and returns the same fiber. It does **not** wait for completion. If
the scope is already closed, the fiber is interrupted immediately.

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

const program = Effect.scoped(
  Effect.gen(function*() {
    const scope = yield* Effect.scope
    const fiber = yield* Effect.forkDetach(Effect.sleep("10 seconds"))
    // Tie the detached fiber's lifetime back to this scope.
    Fiber.runIn(fiber, scope)
    return "registered" // fiber is interrupted when the scope closes
  })
)

Effect.runFork(program)
```

### The `Fiber` interface

Beyond the functions above, a `Fiber<A, E>` exposes runtime fields. Most code
never touches these — they exist for schedulers, tracing, and runtime
integrations. The unsafe methods bypass Effect's sequencing guarantees; prefer
the module functions above.

- **`id`** — the fiber's numeric identifier.
- **`currentOpCount`** — operations executed since the last yield; the scheduler
  compares this against `maxOpsBeforeYield`.
- **`getRef(ref)`** — read a `Context.Reference` value for this fiber.
- **`context`** — the fiber's `Context` (and `setContext` to replace it).
- **`currentScheduler`** — the [`Scheduler`](#scheduler) driving this fiber.
- **`addObserver(cb)`** — register a callback invoked with the fiber's `Exit`
  when it completes; returns a function that removes the observer.
- **`interruptUnsafe(fiberId?, annotations?)`** — immediately request
  interruption without Effect sequencing.
- **`pollUnsafe()`** — return the fiber's `Exit` if it has already completed,
  otherwise `undefined`.

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

const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(Effect.succeed(42))

  // Observe completion via a low-level callback.
  fiber.addObserver((exit) => {
    console.log(exit) // => Exit.succeed(42)
  })

  // Has it finished yet? (likely undefined right after forking)
  fiber.pollUnsafe() // => undefined | Exit<42, never>
})

Effect.runFork(program)
```

## Scheduler reference

The **scheduler** is the runtime piece that decides how runnable tasks are
enqueued, when they are dispatched, and whether a fiber should **yield** after
using up its operation budget. This is advanced runtime tuning — you rarely
provide a custom scheduler, but the references below let you tune fairness.
**Note:** Scheduler priorities affect the *order of queued runtime tasks*, not the
  semantic result of an `Effect`. Lower priority numbers run first; equal
  priorities run FIFO.

### Scheduler

The `Scheduler` interface, also exported as a `Context.Reference` so you can
provide a custom implementation. A scheduler has an `executionMode`
(`"sync" | "async"`), a `shouldYield(fiber)` predicate, and `makeDispatcher()`.

```ts
import { Effect } from "effect"
import { Scheduler, MixedScheduler } from "effect/Scheduler"

// Run with a synchronous scheduler (drains work eagerly).
const program = Effect.succeed(1).pipe(
  Effect.provideService(Scheduler, new MixedScheduler("sync"))
)

Effect.runFork(program)
```

### MixedScheduler

The default scheduler. It batches queued tasks and dispatches them by priority
(FIFO within a priority), supports `"sync"` and `"async"` execution modes, and
yields a fiber once `fiber.currentOpCount >= fiber.maxOpsBeforeYield`.

```ts
import { MixedScheduler } from "effect/Scheduler"

const scheduler = new MixedScheduler("async") // the runtime default
scheduler.executionMode // => "async"
```

### SchedulerDispatcher

Created by a scheduler via `makeDispatcher()`. `scheduleTask(task, priority)`
enqueues work; `flush()` drains pending work synchronously for deterministic
completion.

```ts
import { MixedScheduler } from "effect/Scheduler"

const dispatcher = new MixedScheduler().makeDispatcher()
dispatcher.scheduleTask(() => console.log("ran"), 0)
dispatcher.flush() // => logs "ran"
```

### MaxOpsBeforeYield

`Context.Reference<number>` controlling how many operations a fiber may perform
before the scheduler makes it yield. Default `2048`. Lower it for more frequent
yielding (fairer to other fibers), raise it for throughput on CPU-bound work.

```ts
import { Effect } from "effect"
import { MaxOpsBeforeYield } from "effect/Scheduler"

const program = Effect.succeed("work").pipe(
  Effect.provideService(MaxOpsBeforeYield, 256) // yield more often
)

Effect.runFork(program)
```

### PreventSchedulerYield

`Context.Reference<boolean>` that, when `true`, makes the run loop skip
`Scheduler.shouldYield` entirely. Default `false`. Bypassing yields can improve
throughput for controlled workloads but lets long-running fibers monopolize the
JavaScript thread.

```ts
import { Effect } from "effect"
import { PreventSchedulerYield } from "effect/Scheduler"

const program = Effect.succeed("hot loop").pipe(
  Effect.provideService(PreventSchedulerYield, true) // never yield cooperatively
)

Effect.runFork(program)
```
**Tip:** See [Interruption](https://effect.plants.sh/concurrency/interruption/) for the cooperative
  cancellation model, and [fiber collections](https://effect.plants.sh/concurrency/fiber-collections/)
  (`FiberHandle`, `FiberMap`, `FiberSet`) for managing dynamic sets of fibers.