Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

FiberHandle

Manages at most one fiber inside a scope.

A FiberHandle<A, E> can hold one Fiber<A, E>. Installing a new fiber interrupts the previous one unless the operation is configured with onlyIfMissing, and closing the owning scope interrupts the current fiber. This module includes constructors for handles and scoped runtimes, helpers for setting, reading, clearing, and running fibers, and operations for joining the current fiber or waiting until the handle is empty.

15 exports Since v2.0.0 Source

Combinators

Waits for the fiber in the FiberHandle to complete.

Signature

declare function awaitEmpty<A, E>(self: FiberHandle<A, E>): Effect<void, E>

Example Waiting for a fiber to complete

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
yield* FiberHandle.run(handle, Effect.yieldNow)
// Wait for the fiber to complete
yield* FiberHandle.awaitEmpty(handle)
return yield* FiberHandle.get(handle)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => Option.none()
Since v2.0.0

Interrupts the fiber currently stored in the FiberHandle, if any, and leaves the handle empty.

Signature

declare function clear<A, E>(self: FiberHandle<A, E>): Effect<void>

Example Clearing a fiber handle

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// Add a fiber
yield* FiberHandle.run(handle, Effect.never)
// Clear the handle, interrupting the fiber
yield* FiberHandle.clear(handle)
// The handle is now empty
return FiberHandle.getUnsafe(handle)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => Option.none()
Since v2.0.0

Retrieves the fiber from the FiberHandle effectfully.

Signature

declare function get<A, E>(self: FiberHandle<A, E>): Effect<Option<Fiber<A, E>>>

Example Reading the current fiber

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// Add a fiber
yield* FiberHandle.run(handle, Effect.never)
// Get the current fiber if present
const fiber = yield* FiberHandle.get(handle)
return Option.map(fiber, () => true)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => Option.some(true)

Retrieves the fiber from the FiberHandle synchronously.

When to use

Use when synchronous inspection of the current fiber is needed and an Option result is enough outside the Effect workflow.

Signature

declare function getUnsafe<A, E>(self: FiberHandle<A, E>): Option<Fiber<A, E>>

Example Reading the current fiber unsafely

import { Effect, FiberHandle, Option } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// No fiber initially
const emptyFiber = FiberHandle.getUnsafe(handle)
// Add a fiber
yield* FiberHandle.run(handle, Effect.never)
const fiber = FiberHandle.getUnsafe(handle)
return [emptyFiber, Option.map(fiber, () => true)]
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => [Option.none(), Option.some(true)]
Since v2.0.0

Waits for the FiberHandle to fail or close.

Details

The returned Effect fails with the first managed fiber failure that is not ignored by the handle's interruption rules. Normal successful completion of a managed fiber only removes it from the handle; use awaitEmpty to wait for the current fiber to finish.

Signature

declare function join<A, E>(self: FiberHandle<A, E>): Effect<void, E>

Example Propagating fiber failures

import { Effect, Exit, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
yield* FiberHandle.set(handle, Effect.runFork(Effect.fail("error")))
// parent fiber will fail with "error"
yield* FiberHandle.join(handle)
})
const actual = await Effect.runPromise(Effect.exit(Effect.scoped(program)))
actual // => Exit.fail("error")
Since v2.0.0

Forks an Effect and stores the resulting fiber in the FiberHandle.

Details

The handle manages only one fiber: running a new effect interrupts the previous fiber unless onlyIfMissing is set. When the managed fiber completes, it is removed from the handle. Set startImmediately: false to defer startup. By default, the effect starts immediately.

Signature

declare const run: {
<A, E>(self: FiberHandle<A, E>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly startImmediately?: boolean;
}): <R, XE, XA>(effect: Effect<XA, XE, R>) => Effect<Fiber<XA, XE>, never, R>;
<A, E, R, XE, XA>(self: FiberHandle<A, E>, effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly startImmediately?: boolean;
}): Effect<Fiber<XA, XE>, never, R>;
}

Example Running an effect in a fiber handle

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// Run an effect and get the fiber
const fiber = yield* FiberHandle.run(handle, Effect.succeed("hello"))
const result = yield* Fiber.join(fiber)
// Running another effect will interrupt the previous one
const fiber2 = yield* FiberHandle.run(handle, Effect.succeed("world"))
const result2 = yield* Fiber.join(fiber2)
return [result, result2]
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => ["hello", "world"]
Since v2.0.0

Captures the current runtime and returns a function for forking effects into an existing FiberHandle.

Details

Each call returns the forked fiber, stores it in the handle, and interrupts the previous fiber unless onlyIfMissing is set.

Signature

declare const runtime: <A, E>(self: FiberHandle<A, E>) => <R = never>() => Effect.Effect<<XE extends E, XA extends A>(effect: Effect.Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Fiber.Fiber<XA, XE>, never, R>

Example Capturing a runtime for fiber handles

import { Context, Effect, Fiber, FiberHandle } from "effect"
class Users extends Context.Service<Users, {
readonly getAll: Effect.Effect<Array<unknown>>
}>()("Users") {}
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const run = yield* FiberHandle.runtime(handle)<Users>()
// run an effect and set the fiber in the handle
const fiberA = run(Effect.andThen(Users, (_) => _.getAll))
// this will interrupt the previous fiber
const fiberB = run(Effect.andThen(Users, (_) => _.getAll))
yield* Fiber.await(fiberA)
return (yield* Fiber.join(fiberB)).length
}).pipe(
Effect.scoped // The fiber will be interrupted when the scope is closed
)
const actual = await Effect.runPromise(Effect.provideService(program, Users, {
getAll: Effect.succeed([])
}))
actual // => 0

Captures the current runtime and returns a function for running effects in an existing FiberHandle as Promises.

Details

Each call stores the forked fiber in the handle and interrupts the previous fiber unless onlyIfMissing is set. The Promise resolves with the effect's success value or rejects with the squashed failure cause.

Signature

declare function runtimePromise<A, E>(self: FiberHandle<A, E>): <R = never>() => Effect<<XE, XA>(effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Promise<XA>, never, R>

Example Capturing a runtime for promises

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const runPromise = yield* FiberHandle.runtimePromise(handle)<never>()
// Run an effect and get a promise
const promise = runPromise(Effect.succeed("hello"))
return yield* Effect.promise(() => promise)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"
Since v2.0.0

Sets the fiber in the FiberHandle.

Details

When the fiber completes, it will be removed from the FiberHandle. If a fiber already exists in the FiberHandle, it will be interrupted unless options.onlyIfMissing is set.

Signature

declare const set: {
<A, E, XE, XA>(fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): (self: FiberHandle<A, E>) => Effect<void>;
<A, E, XE, XA>(self: FiberHandle<A, E>, fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): Effect<void>;
}

Example Setting a fiber safely

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const fiber = Effect.runFork(Effect.succeed("hello"))
// Set the fiber safely
yield* FiberHandle.set(handle, fiber)
// The fiber is now managed by the handle
return yield* Fiber.join(fiber)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"

Sets the fiber in a FiberHandle. When the fiber completes, it will be removed from the FiberHandle. If a fiber is already running, it will be interrupted unless options.onlyIfMissing is set.

When to use

Use when an existing forked fiber must be installed synchronously into a handle and immediate interruption of replaced or closed fibers is acceptable.

Signature

declare const setUnsafe: {
<A, E, XE, XA>(fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): (self: FiberHandle<A, E>) => void;
<A, E, XE, XA>(self: FiberHandle<A, E>, fiber: Fiber<XA, XE>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
}): void;
}

Example Setting a fiber unsafely

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
const fiber = Effect.runFork(Effect.succeed("hello"))
// Set the fiber directly (unsafe)
FiberHandle.setUnsafe(handle, fiber)
// The fiber is now managed by the handle
return yield* Fiber.join(fiber)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"

Constructors

Since v2.0.0

Creates a scoped FiberHandle that can store a single fiber.

Details

When the associated Scope is closed, the contained fiber will be interrupted. You can add a fiber to the handle using FiberHandle.run, and the fiber will be automatically removed from the FiberHandle when it completes.

Signature

declare function make<A = unknown, E = unknown>(): Effect<FiberHandle<A, E>, never, Scope>

Example Creating a scoped fiber handle

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
// run some effects
yield* FiberHandle.run(handle, Effect.never)
// this will interrupt the previous fiber
yield* FiberHandle.run(handle, Effect.never)
yield* Effect.yieldNow
return handle.state._tag === "Open" && handle.state.fiber !== undefined
}).pipe(
Effect.scoped // The fiber will be interrupted when the scope is closed
)
const actual = await Effect.runPromise(program)
actual // => true

Creates a scoped run function that forks effects into a new FiberHandle.

Details

Each call returns the forked fiber, stores it in the handle, and interrupts the previous fiber unless onlyIfMissing is set. The managed fiber is interrupted when the handle's scope closes.

Signature

declare function makeRuntime<R, E = unknown, A = unknown>(): Effect<<XE, XA>(effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Fiber<XA, XE>, never, Scope | R>

Example Running effects with a fiber handle

import { Cause, Effect, Exit, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const run = yield* FiberHandle.makeRuntime<never>()
// Run effects and get fibers back
const fiberA = run(Effect.never)
const fiberB = run(Effect.succeed("second"))
// The second fiber will interrupt the first
const resultA = yield* Fiber.await(fiberA)
const resultB = yield* Fiber.await(fiberB)
return [resultA, resultB]
}).pipe(Effect.scoped)
const actual = await Effect.runPromise(program)
actual // => [Exit.failCause(Cause.interrupt(-1)), Exit.succeed("second")]

Creates a scoped run function that forks effects into a new FiberHandle and returns a Promise for each effect result.

When to use

Use when integrating a scoped FiberHandle runner with Promise-based APIs and Promise rejection from squashed failures is the desired boundary.

Details

Each call stores the fiber in the handle and interrupts the previous fiber unless onlyIfMissing is set. The returned Promise resolves with the effect's success value or rejects with the squashed failure cause.

Signature

declare function makeRuntimePromise<R = never, A = unknown, E = unknown>(): Effect<<XE, XA>(effect: Effect<XA, XE, R>, options?: {
readonly onlyIfMissing?: boolean;
readonly propagateInterruption?: boolean;
readonly scheduler?: Scheduler;
readonly signal?: AbortSignal;
}) => Promise<XA>, never, Scope | R>

Example Running effects as promises

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const run = yield* FiberHandle.makeRuntimePromise()
// Run effects and get promises back
const promise = run(Effect.succeed("hello"))
return yield* Effect.promise(() => promise)
}).pipe(Effect.scoped)
const actual = await Effect.runPromise(program)
actual // => "hello"

Guards

Returns true if a value is a FiberHandle by checking for the FiberHandle runtime marker.

Signature

declare function isFiberHandle(u: unknown): u is FiberHandle<unknown, unknown>

Example Checking fiber handles

import { Effect, FiberHandle } from "effect"
const program = Effect.gen(function*() {
const handle = yield* FiberHandle.make()
return [FiberHandle.isFiberHandle(handle), FiberHandle.isFiberHandle("not a handle")]
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => [true, false]

Models

FiberHandle

interface
Since v2.0.0

Scoped handle that manages at most one fiber, interrupts the current fiber when the handle's scope closes, and removes managed fibers from the handle when they complete.

Signature

interface FiberHandle<out A = unknown, out E = unknown> extends Pipeable, Inspectable {
readonly "~effect/FiberHandle": "~effect/FiberHandle";
readonly deferred: Deferred<void, unknown>;
state: {
readonly _tag: "Open";
fiber: Fiber<A, E> | undefined;
} | {
readonly _tag: "Closed";
};
}

Example Managing a single fiber

import { Effect, Fiber, FiberHandle } from "effect"
const program = Effect.gen(function*() {
// Create a FiberHandle that can hold fibers producing strings
const handle = yield* FiberHandle.make<string, never>()
// The handle can store and manage a single fiber
const fiber = yield* FiberHandle.run(handle, Effect.succeed("hello"))
return yield* Fiber.join(fiber)
})
const actual = await Effect.runPromise(Effect.scoped(program))
actual // => "hello"