Skip to main content
This module provides utilities for working with Fiber, the fundamental unit of concurrency in Effect. Fibers are lightweight, user-space threads that allow multiple Effects to run concurrently with structured concurrency guarantees.

Overview

Fibers are the building blocks of concurrent programming in Effect:
  • Lightweight: Much lighter than OS threads, you can create millions
  • Structured concurrency: Parent fibers manage child fiber lifecycles
  • Cancellation safety: Proper resource cleanup when interrupted
  • Cooperative: Fibers yield control at effect boundaries
  • Traceable: Each fiber has an ID for debugging and monitoring

Basic Usage

import { Console, Effect, Fiber } from "effect"

// Fork an effect to run concurrently
const program = Effect.gen(function*() {
  const fiber = yield* Effect.forkChild(
    Effect.gen(function*() {
      yield* Effect.sleep("2 seconds")
      yield* Console.log("Background task completed")
      return "background result"
    })
  )

  // Do other work while the fiber runs
  yield* Console.log("Doing other work...")
  yield* Effect.sleep("1 second")

  // Wait for the fiber to complete
  const result = yield* Fiber.join(fiber)
  yield* Console.log(`Fiber result: ${result}`)
})

Types

Fiber

interface Fiber<out A, out E = never>
A runtime fiber that executes Effects. Provides:
  • id: Unique fiber identifier
  • currentOpCount: Number of operations executed
  • services: Service context for the fiber
  • currentScheduler: Scheduler managing the fiber

Creating Fibers

Fibers are typically created using Effect.forkChild:
const fiber = yield* Effect.forkChild(myEffect)

Joining Fibers

join

const join: <A, E>(self: Fiber<A, E>) => Effect<A, E>
Waits for a fiber to complete and returns its result. If the fiber fails, the error is propagated.
import { Effect, Fiber } from "effect"

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

await

const await: <A, E>(self: Fiber<A, E>) => Effect<Exit<A, E>>
Waits for a fiber to complete and returns its exit value (includes success, failure, or interruption).
import { Effect, Fiber } from "effect"

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

joinAll

const joinAll: <A extends Iterable<Fiber<any, any>>>(
  self: A
) => Effect<...>
Joins multiple fibers, returning all their results.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function*() {
  const fiber1 = yield* Effect.forkChild(Effect.succeed(1))
  const fiber2 = yield* Effect.forkChild(Effect.succeed(2))
  
  const [result1, result2] = yield* Fiber.joinAll([fiber1, fiber2])
  console.log(result1, result2) // 1, 2
})

Interrupting Fibers

interrupt

const interrupt: <A, E>(self: Fiber<A, E>) => Effect<void>
Interrupts a fiber, causing it to stop executing and clean up resources.
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)
  console.log("Fiber interrupted")
})

interruptAs

const interruptAs: {
  (fiberId: number | undefined): <A, E>(self: Fiber<A, E>) => Effect<void>
  <A, E>(self: Fiber<A, E>, fiberId: number | undefined): Effect<void>
}
Interrupts a fiber with a specific fiber ID as the interruptor.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function*() {
  const targetFiber = yield* Effect.forkChild(
    Effect.delay("5 seconds")(Effect.succeed("task completed"))
  )

  // Interrupt the fiber, specifying fiber ID 123 as the interruptor
  yield* Fiber.interruptAs(targetFiber, 123)
  console.log("Fiber interrupted by fiber #123")
})

interruptAll

const interruptAll: <A extends Iterable<Fiber<any, any>>>(
  fibers: A
) => Effect<void>
Interrupts all fibers in the provided iterable.
import { Console, Effect, Fiber } from "effect"

const program = Effect.gen(function*() {
  const fiber1 = yield* Effect.forkChild(
    Effect.gen(function*() {
      yield* Effect.sleep("5 seconds")
      yield* Console.log("Task 1 completed")
    })
  )

  const fiber2 = yield* Effect.forkChild(
    Effect.gen(function*() {
      yield* Effect.sleep("3 seconds")
      yield* Console.log("Task 2 completed")
    })
  )

  // Interrupt all fibers
  yield* Effect.sleep("1 second")
  yield* Fiber.interruptAll([fiber1, fiber2])
  yield* Console.log("All fibers interrupted")
})

Utilities

isFiber

const isFiber: (u: unknown) => u is Fiber<unknown, unknown>
Type guard to check if a value is a Fiber.
import { Effect, Fiber } from "effect"

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

  console.log(Fiber.isFiber(fiber)) // true
  console.log(Fiber.isFiber("hello")) // false
})

getCurrent

const getCurrent: () => Fiber<any, any> | undefined
Returns the current fiber if called from within a fiber context.
import { Effect, Fiber } from "effect"

const program = Effect.gen(function*() {
  const current = Fiber.getCurrent()
  if (current) {
    console.log(`Current fiber ID: ${current.id}`)
  }
})

Common Patterns

Fork and Join

Start concurrent work and wait for results:
import { Effect, Fiber } from "effect"

const parallelExample = Effect.gen(function*() {
  const task1 = Effect.delay(Effect.succeed("task1"), "1 second")
  const task2 = Effect.delay(Effect.succeed("task2"), "2 seconds")

  const fiber1 = yield* Effect.forkChild(task1)
  const fiber2 = yield* Effect.forkChild(task2)

  const result1 = yield* Fiber.join(fiber1)
  const result2 = yield* Fiber.join(fiber2)
  
  return [result1, result2] // ["task1", "task2"]
})

Parallel Execution

Run multiple tasks concurrently:
import { Effect } from "effect"

const parallelExample = Effect.gen(function*() {
  const tasks = [1, 2, 3, 4, 5].map((n) =>
    Effect.gen(function*() {
      yield* Effect.sleep(`${n * 100} millis`)
      return n * n
    })
  )

  // Run all tasks in parallel
  const results = yield* Effect.all(tasks, { concurrency: "unbounded" })
  return results // [1, 4, 9, 16, 25]
})

Scope Management

runIn

const runIn: {
  (scope: Scope): <A, E>(self: Fiber<A, E>) => Fiber<A, E>
  <A, E>(self: Fiber<A, E>, scope: Scope): Fiber<A, E>
}
Links the lifetime of a fiber to a provided scope.

Build docs developers (and LLMs) love