Skip to main content
A Fiber is a lightweight thread of execution that never consumes more than a whole thread. Fibers are spawned by forking effects and run concurrently with the parent effect.

Type Signature

interface Fiber<out A, out E = never> extends Effect<A, E> {
  id(): FiberId
  readonly await: Effect<Exit<A, E>>
  readonly poll: Effect<Option<Exit<A, E>>>
  interruptAsFork(fiberId: FiberId): Effect<void>
}

interface RuntimeFiber<out A, out E = never> extends Fiber<A, E> {
  readonly status: Effect<FiberStatus>
  readonly runtimeFlags: Effect<RuntimeFlags>
}

Creating Fibers

fork

Forks an effect into a new fiber.
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(Effect.succeed(42))
  const result = yield* Fiber.await(fiber)
  console.log(result)
})

forkDaemon

Forks a daemon fiber that’s not supervised by the current scope.
import { Effect } from "effect"

const daemon = Effect.forkDaemon(
  Effect.forever(Effect.sleep("1 second"))
)

forkScoped

Forks a fiber within a scope, ensuring cleanup.
import { Effect } from "effect"

const program = Effect.scoped(
  Effect.gen(function* () {
    const fiber = yield* Effect.forkScoped(Effect.succeed(42))
    return yield* Fiber.await(fiber)
  })
)

Fiber Operations

await

Waits for a fiber to complete and returns its result.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(Effect.succeed(42))
  const exit = yield* Fiber.await(fiber)
  
  console.log(exit)
  // { _tag: 'Success', value: 42 }
})

join

Waits for a fiber and extracts the success value or fails with the error.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(Effect.succeed(42))
  const result = yield* Fiber.join(fiber)
  
  console.log(result)  // 42
})

poll

Checks if a fiber has completed without blocking.
import { Effect, Fiber, Option } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(
    Effect.sleep("1 second").pipe(Effect.as(42))
  )
  
  const result = yield* Fiber.poll(fiber)
  
  if (Option.isSome(result)) {
    console.log("Fiber completed:", result.value)
  } else {
    console.log("Fiber still running")
  }
})

Interruption

interrupt

Interrupts a fiber, safely releasing all resources.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(
    Effect.forever(Effect.sleep("1 second"))
  )
  
  yield* Effect.sleep("5 seconds")
  yield* Fiber.interrupt(fiber)
})

interruptFork

Interrupts a fiber in the background without waiting.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(
    Effect.forever(Effect.sleep("1 second"))
  )
  
  yield* Fiber.interruptFork(fiber)
  // Continues immediately without waiting
})

Combining Fibers

zip

Combines two fibers into a single fiber producing a tuple.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber1 = yield* Effect.fork(Effect.succeed(1))
  const fiber2 = yield* Effect.fork(Effect.succeed("hello"))
  
  const combined = Fiber.zip(fiber1, fiber2)
  const result = yield* Fiber.join(combined)
  
  console.log(result)  // [1, "hello"]
})

race

Races two fibers, returning the result of the first to complete.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber1 = yield* Effect.fork(
    Effect.sleep("2 seconds").pipe(Effect.as(1))
  )
  const fiber2 = yield* Effect.fork(
    Effect.sleep("1 second").pipe(Effect.as(2))
  )
  
  const winner = Fiber.race(fiber1, fiber2)
  const result = yield* Fiber.join(winner)
  
  console.log(result)  // 2
})

Fiber Inspection

status

Retrieves the current status of a fiber.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(Effect.sleep("1 second"))
  const status = yield* fiber.status
  
  console.log(status)
  // Running or Done
})

children

Retrieves all child fibers.
import { Effect } from "effect"

const program = Effect.gen(function* () {
  const fiber = yield* Effect.fork(
    Effect.all([
      Effect.fork(Effect.succeed(1)),
      Effect.fork(Effect.succeed(2))
    ])
  )
  
  const children = yield* fiber.children
  console.log(children.length)  // 2
})

Concurrency Control

all

Runs multiple effects concurrently as fibers.
import { Effect } from "effect"

const program = Effect.all([
  Effect.sleep("1 second").pipe(Effect.as(1)),
  Effect.sleep("1 second").pipe(Effect.as(2)),
  Effect.sleep("1 second").pipe(Effect.as(3))
], { concurrency: "unbounded" })

// All complete in ~1 second instead of 3

raceAll

Races multiple effects, returning the first to complete.
import { Effect } from "effect"

const program = Effect.raceAll([
  Effect.sleep("3 seconds").pipe(Effect.as(1)),
  Effect.sleep("1 second").pipe(Effect.as(2)),
  Effect.sleep("2 seconds").pipe(Effect.as(3))
])

Effect.runPromise(program).then(console.log)
// 2 (after 1 second)
Fibers are automatically interrupted when their parent scope ends, ensuring proper resource cleanup. Use forkDaemon for long-running background tasks.

Use Cases

  • Concurrent request processing
  • Background tasks
  • Timeout handling
  • Parallelizing independent computations
  • Managing long-running operations

Key Operations

OperationDescription
forkCreates a new fiber
awaitWaits for fiber completion
joinWaits and extracts the value
interruptStops a fiber gracefully
pollChecks completion without blocking
zipCombines two fibers
raceRaces fibers to completion
statusGets current fiber status
childrenLists child fibers

Build docs developers (and LLMs) love