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.
Combinators
awaitEmpty
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()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()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)]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")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.
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"]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 // => 0runtimePromise
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"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
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 // => truemakeRuntime
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")]makeRuntimePromise
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
isFiberHandle
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
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"