Skip to content

监督者

Supervisor<A> 是 Effect 中管理 fiber 的实用工具,允许您跟踪它们的生命周期(创建和终止)并产生反映此监督的类型 A 的值。当您需要洞察或控制应用程序中 fiber 的行为时,监督者很有用。

要创建监督者,您可以使用 Supervisor.track 函数。这会生成一个新的监督者,跟踪其子 fiber,将它们维护在一个集合中。这允许您在执行期间观察和监控它们的状态。

您可以使用 Effect.supervised 函数监督 effect。此函数接受监督者作为参数,并返回一个 effect,其中在其内部分叉的所有子 fiber 都由提供的监督者监督。这使您能够通过监督者捕获有关这些子 fiber 的详细信息,例如它们的状态。

示例(监控 Fiber 数量)

在此示例中,我们将使用监督者定期监控应用程序中运行的 fiber 数量。程序计算斐波那契数,在此过程中生成多个 fiber,同时单独的监视器跟踪 fiber 数量。

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Supervisor
Supervisor
,
import Schedule
Schedule
,
import Fiber
Fiber
,
import FiberStatus
FiberStatus
} from "effect"
// 在计算斐波那契数时监控 fiber 的主程序
const
const program: Effect.Effect<void, never, never>
program
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>, never, never>> | YieldWrap<Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>> | YieldWrap<Effect.Effect<number, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>, never, never>> | YieldWrap<Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>> | YieldWrap<...>, void, never>) => Effect.Effect<...> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
// 创建一个监督者来跟踪子 fiber
const
const supervisor: Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>
supervisor
= yield*
import Supervisor
Supervisor
.
const track: Effect.Effect<Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>, never, never>

Creates a new supervisor that tracks children in a set.

@since2.0.0

track
// 开始斐波那契计算,由监督者监督
const
const fibFiber: Fiber.RuntimeFiber<number, never>
fibFiber
= yield*
const fib: (n: number) => Effect.Effect<number>
fib
(20).
Pipeable.pipe<Effect.Effect<number, never, never>, Effect.Effect<number, never, never>, Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>>(this: Effect.Effect<number, never, never>, ab: (_: Effect.Effect<number, never, never>) => Effect.Effect<number, never, never>, bc: (_: Effect.Effect<number, never, never>) => Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>): Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const supervised: <Fiber.RuntimeFiber<any, any>[]>(supervisor: Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Supervises child fibers by reporting them to a specified supervisor.

Details

This function takes a supervisor as an argument and returns an effect where all child fibers forked within it are supervised by the provided supervisor. This enables you to capture detailed information about these child fibers, such as their status, through the supervisor.

Example (Monitoring Fiber Count)

import { Effect, Supervisor, Schedule, Fiber, FiberStatus } from "effect"
// Main program that monitors fibers while calculating a Fibonacci number
const program = Effect.gen(function* () {
// Create a supervisor to track child fibers
const supervisor = yield* Supervisor.track
// Start a Fibonacci calculation, supervised by the supervisor
const fibFiber = yield* fib(20).pipe(
Effect.supervised(supervisor),
// Fork the Fibonacci effect into a fiber
Effect.fork
)
// Define a schedule to periodically monitor the fiber count every 500ms
const policy = Schedule.spaced("500 millis").pipe(
Schedule.whileInputEffect((_) =>
Fiber.status(fibFiber).pipe(
// Continue while the Fibonacci fiber is not done
Effect.andThen((status) => status !== FiberStatus.done)
)
)
)
// Start monitoring the fibers, using the supervisor to track the count
const monitorFiber = yield* monitorFibers(supervisor).pipe(
// Repeat the monitoring according to the schedule
Effect.repeat(policy),
// Fork the monitoring into its own fiber
Effect.fork
)
// Join the monitor and Fibonacci fibers to ensure they complete
yield* Fiber.join(monitorFiber)
const result = yield* Fiber.join(fibFiber)
console.log(`fibonacci result: ${result}`)
})
// Function to monitor and log the number of active fibers
const monitorFibers = (
supervisor: Supervisor.Supervisor<Array<Fiber.RuntimeFiber<any, any>>>
): Effect.Effect<void> =>
Effect.gen(function* () {
const fibers = yield* supervisor.value // Get the current set of fibers
console.log(`number of fibers: ${fibers.length}`)
})
// Recursive Fibonacci calculation, spawning fibers for each recursive step
const fib = (n: number): Effect.Effect<number> =>
Effect.gen(function* () {
if (n <= 1) {
return 1
}
yield* Effect.sleep("500 millis") // Simulate work by delaying
// Fork two fibers for the recursive Fibonacci calls
const fiber1 = yield* Effect.fork(fib(n - 2))
const fiber2 = yield* Effect.fork(fib(n - 1))
// Join the fibers to retrieve their results
const v1 = yield* Fiber.join(fiber1)
const v2 = yield* Fiber.join(fiber2)
return v1 + v2 // Combine the results
})
Effect.runPromise(program)
// Output:
// number of fibers: 0
// number of fibers: 2
// number of fibers: 6
// number of fibers: 14
// number of fibers: 30
// number of fibers: 62
// number of fibers: 126
// number of fibers: 254
// number of fibers: 510
// number of fibers: 1022
// number of fibers: 2034
// number of fibers: 3795
// number of fibers: 5810
// number of fibers: 6474
// number of fibers: 4942
// number of fibers: 2515
// number of fibers: 832
// number of fibers: 170
// number of fibers: 18
// number of fibers: 0
// fibonacci result: 10946

@since2.0.0

supervised
(
const supervisor: Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>
supervisor
),
// 将斐波那契 effect 分叉到 fiber 中
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<Fiber.RuntimeFiber<A, E>, never, R>

Creates a new fiber to run an effect concurrently.

Details

This function takes an effect and forks it into a separate fiber, allowing it to run concurrently without blocking the original effect. The new fiber starts execution immediately after being created, and the fiber object is returned immediately without waiting for the effect to begin. This is useful when you want to run tasks concurrently while continuing other tasks in the parent fiber.

The forked fiber is attached to the parent fiber's scope. This means that when the parent fiber terminates, the child fiber will also be terminated automatically. This feature, known as "auto supervision," ensures that no fibers are left running unintentionally. If you prefer not to have this auto supervision behavior, you can use

forkDaemon

or

forkIn

.

When to Use

Use this function when you need to run an effect concurrently without blocking the current execution flow. For example, you might use it to launch background tasks or concurrent computations. However, working with fibers can be complex, so before using this function directly, you might want to explore higher-level functions like

raceWith

,

zip

, or others that can manage concurrency for you.

Example

import { Effect } from "effect"
const fib = (n: number): Effect.Effect<number> =>
n < 2
? Effect.succeed(n)
: Effect.zipWith(fib(n - 1), fib(n - 2), (a, b) => a + b)
// ┌─── Effect<RuntimeFiber<number, never>, never, never>
// ▼
const fib10Fiber = Effect.fork(fib(10))

@seeforkWithErrorHandler for a version that allows you to handle errors.

@since2.0.0

fork
)
// 定义一个调度,每 500ms 定期监控 fiber 数量
const
const policy: Schedule.Schedule<number, unknown, never>
policy
=
import Schedule
Schedule
.
const spaced: (duration: DurationInput) => Schedule.Schedule<number>

Returns a schedule that recurs continuously, with each repetition spaced by the specified duration from the last run.

Details

This schedule ensures that executions occur at a fixed interval, maintaining a consistent delay between repetitions. The delay starts from the end of the last execution, not from the schedule start time.

@seefixed If you need to run at a fixed interval from the start.

@since2.0.0

spaced
("500 millis").
Pipeable.pipe<Schedule.Schedule<number, unknown, never>, Schedule.Schedule<number, unknown, never>>(this: Schedule.Schedule<number, unknown, never>, ab: (_: Schedule.Schedule<number, unknown, never>) => Schedule.Schedule<number, unknown, never>): Schedule.Schedule<number, unknown, never> (+21 overloads)
pipe
(
import Schedule
Schedule
.
const whileInputEffect: <unknown, never>(f: (input: unknown) => Effect.Effect<boolean, never, never>) => <Out, R>(self: Schedule.Schedule<Out, unknown, R>) => Schedule.Schedule<Out, unknown, R> (+1 overload)

Returns a new schedule that continues execution for as long as the given effectful predicate on the input evaluates to true.

Details

This function modifies an existing schedule so that it only continues execution while an effectful predicate holds true for its input. If the predicate evaluates to false at any step, the schedule stops.

@seewhileInput If you need to use a pure predicate.

@since2.0.0

whileInputEffect
((
_: unknown
_
) =>
import Fiber
Fiber
.
const status: <number, never>(self: Fiber.RuntimeFiber<number, never>) => Effect.Effect<FiberStatus.FiberStatus>

Returns the FiberStatus of a RuntimeFiber.

@since2.0.0

status
(
const fibFiber: Fiber.RuntimeFiber<number, never>
fibFiber
).
Pipeable.pipe<Effect.Effect<FiberStatus.FiberStatus, never, never>, Effect.Effect<boolean, never, never>>(this: Effect.Effect<FiberStatus.FiberStatus, never, never>, ab: (_: Effect.Effect<FiberStatus.FiberStatus, never, never>) => Effect.Effect<boolean, never, never>): Effect.Effect<boolean, never, never> (+21 overloads)
pipe
(
// 在斐波那契 fiber 未完成时继续
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const andThen: <FiberStatus.FiberStatus, boolean>(f: (a: FiberStatus.FiberStatus) => boolean) => <E, R>(self: Effect.Effect<FiberStatus.FiberStatus, E, R>) => Effect.Effect<boolean, E, R> (+3 overloads)

Chains two actions, where the second action can depend on the result of the first.

Syntax

const transformedEffect = pipe(myEffect, Effect.andThen(anotherEffect))
// or
const transformedEffect = Effect.andThen(myEffect, anotherEffect)
// or
const transformedEffect = myEffect.pipe(Effect.andThen(anotherEffect))

When to Use

Use andThen when you need to run multiple actions in sequence, with the second action depending on the result of the first. This is useful for combining effects or handling computations that must happen in order.

Details

The second action can be:

  • A constant value (similar to

as

)

  • A function returning a value (similar to

map

)

  • A Promise
  • A function returning a Promise
  • An Effect
  • A function returning an Effect (similar to

flatMap

)

Note: andThen works well with both Option and Either types, treating them as effects.

Example (Applying a Discount Based on Fetched Amount)

import { pipe, Effect } from "effect"
// Function to apply a discount safely to a transaction amount
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
// Simulated asynchronous task to fetch a transaction amount from database
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
// Using Effect.map and Effect.flatMap
const result1 = pipe(
fetchTransactionAmount,
Effect.map((amount) => amount * 2),
Effect.flatMap((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result1).then(console.log)
// Output: 190
// Using Effect.andThen
const result2 = pipe(
fetchTransactionAmount,
Effect.andThen((amount) => amount * 2),
Effect.andThen((amount) => applyDiscount(amount, 5))
)
Effect.runPromise(result2).then(console.log)
// Output: 190

@since2.0.0

andThen
((
status: FiberStatus.FiberStatus
status
) =>
status: FiberStatus.FiberStatus
status
!==
import FiberStatus
FiberStatus
.
const done: FiberStatus.FiberStatus

@since2.0.0

done
)
)
)
)
// 开始监控 fiber,使用监督者跟踪数量
const
const monitorFiber: Fiber.RuntimeFiber<number, never>
monitorFiber
= yield*
const monitorFibers: (supervisor: Supervisor.Supervisor<Array<Fiber.RuntimeFiber<any, any>>>) => Effect.Effect<void>
monitorFibers
(
const supervisor: Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>
supervisor
).
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<number, never, never>, Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<number, never, never>, bc: (_: Effect.Effect<number, never, never>) => Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>): Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never> (+21 overloads)
pipe
(
// 根据调度重复监控
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const repeat: <number, unknown, never>(schedule: Schedule.Schedule<number, unknown, never>) => <E, R>(self: Effect.Effect<unknown, E, R>) => Effect.Effect<number, E, R> (+3 overloads)

Repeats an effect based on a specified schedule or until the first failure.

Details

This function executes an effect repeatedly according to the given schedule. Each repetition occurs after the initial execution of the effect, meaning that the schedule determines the number of additional repetitions. For example, using Schedule.once will result in the effect being executed twice (once initially and once as part of the repetition).

If the effect succeeds, it is repeated according to the schedule. If it fails, the repetition stops immediately, and the failure is returned.

The schedule can also specify delays between repetitions, making it useful for tasks like retrying operations with backoff, periodic execution, or performing a series of dependent actions.

You can combine schedules for more advanced repetition logic, such as adding delays, limiting recursions, or dynamically adjusting based on the outcome of each execution.

Example (Success Example)

import { Effect, Schedule, Console } from "effect"
const action = Console.log("success")
const policy = Schedule.addDelay(Schedule.recurs(2), () => "100 millis")
const program = Effect.repeat(action, policy)
Effect.runPromise(program).then((n) => console.log(`repetitions: ${n}`))

Example (Failure Example)

import { Effect, Schedule } from "effect"
let count = 0
// Define an async effect that simulates an action with possible failures
const action = Effect.async<string, string>((resume) => {
if (count > 1) {
console.log("failure")
resume(Effect.fail("Uh oh!"))
} else {
count++
console.log("success")
resume(Effect.succeed("yay!"))
}
})
const policy = Schedule.addDelay(Schedule.recurs(2), () => "100 millis")
const program = Effect.repeat(action, policy)
Effect.runPromiseExit(program).then(console.log)

@since2.0.0

repeat
(
const policy: Schedule.Schedule<number, unknown, never>
policy
),
// 将监控分叉到自己的 fiber 中
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<Fiber.RuntimeFiber<A, E>, never, R>

Creates a new fiber to run an effect concurrently.

Details

This function takes an effect and forks it into a separate fiber, allowing it to run concurrently without blocking the original effect. The new fiber starts execution immediately after being created, and the fiber object is returned immediately without waiting for the effect to begin. This is useful when you want to run tasks concurrently while continuing other tasks in the parent fiber.

The forked fiber is attached to the parent fiber's scope. This means that when the parent fiber terminates, the child fiber will also be terminated automatically. This feature, known as "auto supervision," ensures that no fibers are left running unintentionally. If you prefer not to have this auto supervision behavior, you can use

forkDaemon

or

forkIn

.

When to Use

Use this function when you need to run an effect concurrently without blocking the current execution flow. For example, you might use it to launch background tasks or concurrent computations. However, working with fibers can be complex, so before using this function directly, you might want to explore higher-level functions like

raceWith

,

zip

, or others that can manage concurrency for you.

Example

import { Effect } from "effect"
const fib = (n: number): Effect.Effect<number> =>
n < 2
? Effect.succeed(n)
: Effect.zipWith(fib(n - 1), fib(n - 2), (a, b) => a + b)
// ┌─── Effect<RuntimeFiber<number, never>, never, never>
// ▼
const fib10Fiber = Effect.fork(fib(10))

@seeforkWithErrorHandler for a version that allows you to handle errors.

@since2.0.0

fork
)
// 连接监视器和斐波那契 fiber 以确保它们完成
yield*
import Fiber
Fiber
.
const join: <number, never>(self: Fiber.Fiber<number, never>) => Effect.Effect<number, never, never>

Joins the fiber, which suspends the joining fiber until the result of the fiber has been determined. Attempting to join a fiber that has erred will result in a catchable error. Joining an interrupted fiber will result in an "inner interruption" of this fiber, unlike interruption triggered by another fiber, "inner interruption" can be caught and recovered.

@since2.0.0

join
(
const monitorFiber: Fiber.RuntimeFiber<number, never>
monitorFiber
)
const
const result: number
result
= yield*
import Fiber
Fiber
.
const join: <number, never>(self: Fiber.Fiber<number, never>) => Effect.Effect<number, never, never>

Joins the fiber, which suspends the joining fiber until the result of the fiber has been determined. Attempting to join a fiber that has erred will result in a catchable error. Joining an interrupted fiber will result in an "inner interruption" of this fiber, unlike interruption triggered by another fiber, "inner interruption" can be caught and recovered.

@since2.0.0

join
(
const fibFiber: Fiber.RuntimeFiber<number, never>
fibFiber
)
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(`fibonacci result: ${
const result: number
result
}`)
})
// 监控并记录活动 fiber 数量的函数
const
const monitorFibers: (supervisor: Supervisor.Supervisor<Array<Fiber.RuntimeFiber<any, any>>>) => Effect.Effect<void>
monitorFibers
= (
supervisor: Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>
supervisor
:
import Supervisor
Supervisor
.
interface Supervisor<out T>

@since2.0.0

@since2.0.0

Supervisor
<
interface Array<T>
Array
<
import Fiber
Fiber
.
interface RuntimeFiber<out A, out E = never>

A runtime fiber that is executing an effect. Runtime fibers have an identity and a trace.

@since2.0.0

RuntimeFiber
<any, any>>>
):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<void> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<Fiber.RuntimeFiber<any, any>[], never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Fiber.RuntimeFiber<any, any>[], never, never>>, void, never>) => Effect.Effect<void, never, never> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
const
const fibers: Fiber.RuntimeFiber<any, any>[]
fibers
= yield*
supervisor: Supervisor.Supervisor<Fiber.RuntimeFiber<any, any>[]>
supervisor
.
Supervisor<RuntimeFiber<any, any>[]>.value: Effect.Effect<Fiber.RuntimeFiber<any, any>[], never, never>

Returns an Effect that succeeds with the value produced by this supervisor. This value may change over time, reflecting what the supervisor produces as it supervises fibers.

value
// 获取当前的 fiber 集合
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(`number of fibers: ${
const fibers: Fiber.RuntimeFiber<any, any>[]
fibers
.
Array<RuntimeFiber<any, any>>.length: number

Gets or sets the length of the array. This is a number one higher than the highest index in the array.

length
}`)
})
// 递归斐波那契计算,为每个递归步骤生成 fiber
const
const fib: (n: number) => Effect.Effect<number>
fib
= (
n: number
n
: number):
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<number> =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, number>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, never, never>>, number, never>) => Effect.Effect<number, never, never> (+1 overload)

Provides a way to write effectful code using generator functions, simplifying control flow and error handling.

When to Use

Effect.gen allows you to write code that looks and behaves like synchronous code, but it can handle asynchronous tasks, errors, and complex control flow (like loops and conditions). It helps make asynchronous code more readable and easier to manage.

The generator functions work similarly to async/await but with more explicit control over the execution of effects. You can yield* values from effects and return the final result at the end.

Example

import { Effect } from "effect"
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, Error> =>
discountRate === 0
? Effect.fail(new Error("Discount rate cannot be zero"))
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function* () {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})

@since2.0.0

gen
(function* () {
if (
n: number
n
<= 1) {
return 1
}
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sleep: (duration: DurationInput) => Effect.Effect<void>

Suspends the execution of an effect for a specified Duration.

Details

This function pauses the execution of an effect for a given duration. It is asynchronous, meaning that it does not block the fiber executing the effect. Instead, the fiber is suspended during the delay period and can resume once the specified time has passed.

The duration can be specified using various formats supported by the Duration module, such as a string ("2 seconds") or numeric value representing milliseconds.

Example

import { Effect } from "effect"
const program = Effect.gen(function*() {
console.log("Starting task...")
yield* Effect.sleep("3 seconds") // Waits for 3 seconds
console.log("Task completed!")
})
Effect.runFork(program)
// Output:
// Starting task...
// Task completed!

@since2.0.0

sleep
("500 millis") // 通过延迟模拟工作
// 为递归斐波那契调用分叉两个 fiber
const
const fiber1: Fiber.RuntimeFiber<number, never>
fiber1
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <number, never, never>(self: Effect.Effect<number, never, never>) => Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>

Creates a new fiber to run an effect concurrently.

Details

This function takes an effect and forks it into a separate fiber, allowing it to run concurrently without blocking the original effect. The new fiber starts execution immediately after being created, and the fiber object is returned immediately without waiting for the effect to begin. This is useful when you want to run tasks concurrently while continuing other tasks in the parent fiber.

The forked fiber is attached to the parent fiber's scope. This means that when the parent fiber terminates, the child fiber will also be terminated automatically. This feature, known as "auto supervision," ensures that no fibers are left running unintentionally. If you prefer not to have this auto supervision behavior, you can use

forkDaemon

or

forkIn

.

When to Use

Use this function when you need to run an effect concurrently without blocking the current execution flow. For example, you might use it to launch background tasks or concurrent computations. However, working with fibers can be complex, so before using this function directly, you might want to explore higher-level functions like

raceWith

,

zip

, or others that can manage concurrency for you.

Example

import { Effect } from "effect"
const fib = (n: number): Effect.Effect<number> =>
n < 2
? Effect.succeed(n)
: Effect.zipWith(fib(n - 1), fib(n - 2), (a, b) => a + b)
// ┌─── Effect<RuntimeFiber<number, never>, never, never>
// ▼
const fib10Fiber = Effect.fork(fib(10))

@seeforkWithErrorHandler for a version that allows you to handle errors.

@since2.0.0

fork
(
const fib: (n: number) => Effect.Effect<number>
fib
(
n: number
n
- 2))
const
const fiber2: Fiber.RuntimeFiber<number, never>
fiber2
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fork: <number, never, never>(self: Effect.Effect<number, never, never>) => Effect.Effect<Fiber.RuntimeFiber<number, never>, never, never>

Creates a new fiber to run an effect concurrently.

Details

This function takes an effect and forks it into a separate fiber, allowing it to run concurrently without blocking the original effect. The new fiber starts execution immediately after being created, and the fiber object is returned immediately without waiting for the effect to begin. This is useful when you want to run tasks concurrently while continuing other tasks in the parent fiber.

The forked fiber is attached to the parent fiber's scope. This means that when the parent fiber terminates, the child fiber will also be terminated automatically. This feature, known as "auto supervision," ensures that no fibers are left running unintentionally. If you prefer not to have this auto supervision behavior, you can use

forkDaemon

or

forkIn

.

When to Use

Use this function when you need to run an effect concurrently without blocking the current execution flow. For example, you might use it to launch background tasks or concurrent computations. However, working with fibers can be complex, so before using this function directly, you might want to explore higher-level functions like

raceWith

,

zip

, or others that can manage concurrency for you.

Example

import { Effect } from "effect"
const fib = (n: number): Effect.Effect<number> =>
n < 2
? Effect.succeed(n)
: Effect.zipWith(fib(n - 1), fib(n - 2), (a, b) => a + b)
// ┌─── Effect<RuntimeFiber<number, never>, never, never>
// ▼
const fib10Fiber = Effect.fork(fib(10))

@seeforkWithErrorHandler for a version that allows you to handle errors.

@since2.0.0

fork
(
const fib: (n: number) => Effect.Effect<number>
fib
(
n: number
n
- 1))
// 连接 fiber 以检索它们的结果
const
const v1: number
v1
= yield*
import Fiber
Fiber
.
const join: <number, never>(self: Fiber.Fiber<number, never>) => Effect.Effect<number, never, never>

Joins the fiber, which suspends the joining fiber until the result of the fiber has been determined. Attempting to join a fiber that has erred will result in a catchable error. Joining an interrupted fiber will result in an "inner interruption" of this fiber, unlike interruption triggered by another fiber, "inner interruption" can be caught and recovered.

@since2.0.0

join
(
const fiber1: Fiber.RuntimeFiber<number, never>
fiber1
)
const
const v2: number
v2
= yield*
import Fiber
Fiber
.
const join: <number, never>(self: Fiber.Fiber<number, never>) => Effect.Effect<number, never, never>

Joins the fiber, which suspends the joining fiber until the result of the fiber has been determined. Attempting to join a fiber that has erred will result in a catchable error. Joining an interrupted fiber will result in an "inner interruption" of this fiber, unlike interruption triggered by another fiber, "inner interruption" can be caught and recovered.

@since2.0.0

join
(
const fiber2: Fiber.RuntimeFiber<number, never>
fiber2
)
return
const v1: number
v1
+
const v2: number
v2
// 合并结果
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runPromise: <void, never>(effect: Effect.Effect<void, never, never>, options?: {
readonly signal?: AbortSignal | undefined;
} | undefined) => Promise<void>

Executes an effect and returns the result as a Promise.

Details

This function runs an effect and converts its result into a Promise. If the effect succeeds, the Promise will resolve with the successful result. If the effect fails, the Promise will reject with an error, which includes the failure details of the effect.

The optional options parameter allows you to pass an AbortSignal for cancellation, enabling more fine-grained control over asynchronous tasks.

When to Use

Use this function when you need to execute an effect and work with its result in a promise-based system, such as when integrating with third-party libraries that expect Promise results.

Example (Running a Successful Effect as a Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.succeed(1)).then(console.log)
// Output: 1

Example (Handling a Failing Effect as a Rejected Promise)

import { Effect } from "effect"
Effect.runPromise(Effect.fail("my error")).catch(console.error)
// Output:
// (FiberFailure) Error: my error

@seerunPromiseExit for a version that returns an Exit type instead of rejecting.

@since2.0.0

runPromise
(
const program: Effect.Effect<void, never, never>
program
)
/*
输出:
number of fibers: 0
number of fibers: 2
number of fibers: 6
number of fibers: 14
number of fibers: 30
number of fibers: 62
number of fibers: 126
number of fibers: 254
number of fibers: 510
number of fibers: 1022
number of fibers: 2034
number of fibers: 3795
number of fibers: 5810
number of fibers: 6474
number of fibers: 4942
number of fibers: 2515
number of fibers: 832
number of fibers: 170
number of fibers: 18
number of fibers: 0
fibonacci result: 10946
*/