Browse documentation

Fx

Fx: work arrives

Compose work declaratively over time, with Effect fibers, interruption, and scoped cleanup.

Fx is a declarative way to compose work over time. Describe what starts work, which executions may overlap, when old work becomes obsolete, and when everything should stop. The composed program carries out those rules as values arrive.

Effect supplies fibers, interruption, and scopes. Fx builds on them to coordinate producer executions and their lifetimes: it acts as a fiber-management system over the dimension of time. Its combinators express those relationships without requiring each application to track and interrupt child fibers itself.

An Fx<A, E, R> describes a producer that can emit zero, one, or many A values, report expected failures of type E, and require services R. It is lazy: constructing an Fx starts no subscription. A runner such as Fx.observe returns the Effect that executes it. The owner of that execution also owns interruption and cleanup.

Keep the Fx Marble Atlas nearby to compare values, timing, completion, and interruption visually. Each timeline has Read this diagram help for its notation.

NeedUsePrimary contract
Create a source from data, time, or callbacksBuilding FxConstruct a producer; observation starts its work.
Run or collect a producerConsuming FxChoose completion, result, and ownership.
Combine independent sourcesComposing FxDeclare which arrivals produce output.
Replace, queue, or overlap workHigher-order policiesCoordinate inner fibers and interruption.
Debounce, poll, or detect silenceTime and rateDeclare when work or delivery happens.
Compare operator behavior visuallyFx Marble AtlasSee values, completion, and interruption over time.

Declare how new input changes running work

Effect can interrupt a request. Fx.switchMapEffect(load) composes that capability into a rule: when another input arrives, interrupt the previous inner execution and run load for the new one. concatMap instead waits for each inner to finish; flatMapConcurrently limits how many may run at once. The higher-order operators make these policies part of the workflow’s declaration.

Timing composes the same way. Debounce describes a quiet period before forwarding input; switching describes what that input does to work already in flight. Their placement determines when replacement happens. Interruption still follows Effect’s cleanup semantics; it does not undo an external action that already completed.

Separate events, state, and work

A keystroke is an event. The current query is state. A network request is work. Its result is an event that can update state. An Fx describes the events and work; it does not automatically remember a current value for somebody who subscribes later. Use a RefSubject when current readable state is the capability you need, and a Subject when independently owned code publishes events.

Start with a finite command source so both output and completion are easy to inspect:

import { Effect } from "effect"
import { Fx } from "@typed/fx"

const shortcuts = Fx.fromIterable(["open-search", "", "open-settings"]).pipe(
  Fx.filter((command) => command.length > 0),
  Fx.map((command) => ({ type: "shortcut", command }) as const),
)

const program: Effect.Effect<ReadonlyArray<{ readonly type: "shortcut"; readonly command: string }>> =
  Fx.collectAll(shortcuts)

const values = await Effect.runPromise(program)

// [{ type: "shortcut", command: "open-search" }, { type: "shortcut", command: "open-settings" }]

Running program obtains the iterator, offers open-search, drops the blank command, then offers open-settings. The collector retains both outputs until the iterable completes. Before the runner starts, there are no collected values; the pipeline is a description, not an eagerly mapped array. With an open keyboard listener, the same collector would keep waiting for completion.

A transform wraps delivery: map changes each value before forwarding it to the downstream Sink. It need not allocate an intermediate collection or fork a fiber for each mapped value. Higher-order operators additionally coordinate inner executions; the operator determines that relationship.

Give each subscription an owner

Running an ordinary Fx twice starts its producer twice. Assigning it to a constant does not share work. For a callback source, each subscription registers its own listener and removes that listener when it ends. Building Fx shows that adapter; Subject sharing covers a deliberately shared connection.

Effects and Fx retain their expected failures and required services throughout composition. Services and lifetime explains provisioning, and recovery shows where to handle a failed job so later input survives. Basic Effect composition is the prerequisite for those lessons.

Continue with one decision at a time

Build a source in Building Fx, then learn to consume it, transform values, and combine independent producers. When a value starts work of its own, higher-order work makes the admission policy explicit; time and recovery add the two common boundaries. The API reference is the complete operator lookup.