Skip to content

信号量

信号量是一种用于管理对共享资源访问的同步机制。在 Effect 中,信号量帮助控制资源访问或在异步并发操作中协调任务。

信号量充当广义的互斥锁,允许同时持有和释放一定数量的许可证。许可证就像门票,为任务或纤程提供对共享资源的受控访问。当没有可用许可证时,尝试获取许可证的任务将等待,直到有许可证被释放。

Effect.makeSemaphore 函数使用指定数量的许可证初始化信号量。 每个许可证允许一个任务访问资源或并发执行操作,多个许可证启用可配置的并发级别。

示例(创建具有 3 个许可证的信号量)

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
// 创建一个具有 3 个许可证的信号量
const
const mutex: Effect.Effect<Effect.Semaphore, never, never>
mutex
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const makeSemaphore: (permits: number) => Effect.Effect<Effect.Semaphore>

Creates a new semaphore with the specified number of permits.

Details

This function initializes a semaphore that controls concurrent access to a shared resource. The number of permits determines how many tasks can access the resource concurrently.

Example

import { Effect } from "effect"
// Create a semaphore with 3 permits
const mutex = Effect.makeSemaphore(3)

@since2.0.0

makeSemaphore
(3)

withPermits 方法让您指定运行 effect 所需的许可证数量。一旦指定的许可证可用,它就会运行 effect,在任务完成时自动释放许可证。

示例(使用单许可证信号量强制顺序任务执行)

在此示例中,三个任务同时启动,但它们按顺序运行,因为单许可证信号量一次只允许一个任务继续。

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
const
const task: Effect.Effect<void, never, never>
task
=
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const gen: <YieldWrap<Effect.Effect<void, never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<void, 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* () {
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level.

Details

This function provides a simple way to log messages or error causes during the execution of your effects. By default, logs are recorded at the INFO level, but this can be adjusted using other logging utilities (Logger.withMinimumLogLevel). Multiple items, including Cause instances, can be logged in a single call. When logging Cause instances, detailed error information is included in the log output.

The log output includes useful metadata like the current timestamp, log level, and fiber ID, making it suitable for debugging and tracking purposes. This function does not interrupt or alter the effect's execution flow.

Example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
("start")
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
("2 seconds")
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level.

Details

This function provides a simple way to log messages or error causes during the execution of your effects. By default, logs are recorded at the INFO level, but this can be adjusted using other logging utilities (Logger.withMinimumLogLevel). Multiple items, including Cause instances, can be logged in a single call. When logging Cause instances, detailed error information is included in the log output.

The log output includes useful metadata like the current timestamp, log level, and fiber ID, making it suitable for debugging and tracking purposes. This function does not interrupt or alter the effect's execution flow.

Example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
("end")
})
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<Effect.Semaphore, never, never>> | YieldWrap<Effect.Effect<[void, void, void], never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Effect.Semaphore, never, never>> | YieldWrap<Effect.Effect<[void, void, void], 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 mutex: Effect.Semaphore
mutex
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const makeSemaphore: (permits: number) => Effect.Effect<Effect.Semaphore>

Creates a new semaphore with the specified number of permits.

Details

This function initializes a semaphore that controls concurrent access to a shared resource. The number of permits determines how many tasks can access the resource concurrently.

Example

import { Effect } from "effect"
// Create a semaphore with 3 permits
const mutex = Effect.makeSemaphore(3)

@since2.0.0

makeSemaphore
(1)
// 包装任务以需要一个许可证,强制顺序执行
const
const semTask: Effect.Effect<void, never, never>
semTask
=
const mutex: Effect.Semaphore
mutex
.
Semaphore.withPermits(permits: number): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>

Runs an effect with the given number of permits and releases the permits when the effect completes.

Details

This function acquires the specified number of permits before executing the provided effect. Once the effect finishes, the permits are released. If insufficient permits are available, the function will wait until they are released by other tasks.

withPermits
(1)(
const task: Effect.Effect<void, never, never>
task
)
.
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<void, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const withLogSpan: (label: string) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Adds a log span to an effect for tracking and logging its execution duration.

Details

This function wraps an effect with a log span, providing performance monitoring and debugging capabilities. The log span tracks the duration of the wrapped effect and logs it with the specified label. This is particularly useful when analyzing time-sensitive operations or understanding the execution time of specific tasks in your application.

The logged output will include the label and the total time taken for the operation. The span information is included in the log metadata, making it easy to trace performance metrics in logs.

Example

import { Effect } from "effect"
const program = Effect.gen(function*() {
yield* Effect.sleep("1 second")
yield* Effect.log("The job is finished!")
}).pipe(Effect.withLogSpan("myspan"))
Effect.runFork(program)
// timestamp=... level=INFO fiber=#0 message="The job is finished!" myspan=1011ms

@since2.0.0

withLogSpan
("elapsed"))
// 并发运行 3 个任务,但由于单许可证信号量
// 它们按顺序执行
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const all: <readonly [Effect.Effect<void, never, never>, Effect.Effect<void, never, never>, Effect.Effect<void, never, never>], {
concurrency: "unbounded";
}>(arg: readonly [Effect.Effect<void, never, never>, Effect.Effect<void, never, never>, Effect.Effect<void, never, never>], options?: {
concurrency: "unbounded";
} | undefined) => Effect.Effect<[void, void, void], never, never>

Combines multiple effects into one, returning results based on the input structure.

Details

Use this function when you need to run multiple effects and combine their results into a single output. It supports tuples, iterables, structs, and records, making it flexible for different input types.

For instance, if the input is a tuple:

// ┌─── a tuple of effects
// ▼
Effect.all([effect1, effect2, ...])

the effects are executed sequentially, and the result is a new effect containing the results as a tuple. The results in the tuple match the order of the effects passed to Effect.all.

Concurrency

You can control the execution order (e.g., sequential vs. concurrent) using the concurrency option.

Short-Circuiting Behavior

This function stops execution on the first error it encounters, this is called "short-circuiting". If any effect in the collection fails, the remaining effects will not run, and the error will be propagated. To change this behavior, you can use the mode option, which allows all effects to run and collect results as Either or Option.

The mode option

The { mode: "either" } option changes the behavior of Effect.all to ensure all effects run, even if some fail. Instead of stopping on the first failure, this mode collects both successes and failures, returning an array of Either instances where each result is either a Right (success) or a Left (failure).

Similarly, the { mode: "validate" } option uses Option to indicate success or failure. Each effect returns None for success and Some with the error for failure.

Example (Combining Effects in Tuples)

import { Effect, Console } from "effect"
const tupleOfEffects = [
Effect.succeed(42).pipe(Effect.tap(Console.log)),
Effect.succeed("Hello").pipe(Effect.tap(Console.log))
] as const
// ┌─── Effect<[number, string], never, never>
// ▼
const resultsAsTuple = Effect.all(tupleOfEffects)
Effect.runPromise(resultsAsTuple).then(console.log)
// Output:
// 42
// Hello
// [ 42, 'Hello' ]

Example (Combining Effects in Iterables)

import { Effect, Console } from "effect"
const iterableOfEffects: Iterable<Effect.Effect<number>> = [1, 2, 3].map(
(n) => Effect.succeed(n).pipe(Effect.tap(Console.log))
)
// ┌─── Effect<number[], never, never>
// ▼
const resultsAsArray = Effect.all(iterableOfEffects)
Effect.runPromise(resultsAsArray).then(console.log)
// Output:
// 1
// 2
// 3
// [ 1, 2, 3 ]

Example (Combining Effects in Structs)

import { Effect, Console } from "effect"
const structOfEffects = {
a: Effect.succeed(42).pipe(Effect.tap(Console.log)),
b: Effect.succeed("Hello").pipe(Effect.tap(Console.log))
}
// ┌─── Effect<{ a: number; b: string; }, never, never>
// ▼
const resultsAsStruct = Effect.all(structOfEffects)
Effect.runPromise(resultsAsStruct).then(console.log)
// Output:
// 42
// Hello
// { a: 42, b: 'Hello' }

Example (Combining Effects in Records)

import { Effect, Console } from "effect"
const recordOfEffects: Record<string, Effect.Effect<number>> = {
key1: Effect.succeed(1).pipe(Effect.tap(Console.log)),
key2: Effect.succeed(2).pipe(Effect.tap(Console.log))
}
// ┌─── Effect<{ [x: string]: number; }, never, never>
// ▼
const resultsAsRecord = Effect.all(recordOfEffects)
Effect.runPromise(resultsAsRecord).then(console.log)
// Output:
// 1
// 2
// { key1: 1, key2: 2 }

Example (Short-Circuiting Behavior)

import { Effect, Console } from "effect"
const program = Effect.all([
Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
// Won't execute due to earlier failure
Effect.succeed("Task3").pipe(Effect.tap(Console.log))
])
Effect.runPromiseExit(program).then(console.log)
// Output:
// Task1
// {
// _id: 'Exit',
// _tag: 'Failure',
// cause: { _id: 'Cause', _tag: 'Fail', failure: 'Task2: Oh no!' }
// }

Example (Collecting Results with mode: "either")

import { Effect, Console } from "effect"
const effects = [
Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
Effect.succeed("Task3").pipe(Effect.tap(Console.log))
]
const program = Effect.all(effects, { mode: "either" })
Effect.runPromiseExit(program).then(console.log)
// Output:
// Task1
// Task3
// {
// _id: 'Exit',
// _tag: 'Success',
// value: [
// { _id: 'Either', _tag: 'Right', right: 'Task1' },
// { _id: 'Either', _tag: 'Left', left: 'Task2: Oh no!' },
// { _id: 'Either', _tag: 'Right', right: 'Task3' }
// ]
// }

Example (Collecting Results with mode: "validate")

import { Effect, Console } from "effect"
const effects = [
Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
Effect.succeed("Task3").pipe(Effect.tap(Console.log))
]
const program = Effect.all(effects, { mode: "validate" })
Effect.runPromiseExit(program).then((result) => console.log("%o", result))
// Output:
// Task1
// Task3
// {
// _id: 'Exit',
// _tag: 'Failure',
// cause: {
// _id: 'Cause',
// _tag: 'Fail',
// failure: [
// { _id: 'Option', _tag: 'None' },
// { _id: 'Option', _tag: 'Some', value: 'Task2: Oh no!' },
// { _id: 'Option', _tag: 'None' }
// ]
// }
// }

@seeforEach for iterating over elements and applying an effect.

@seeallWith for a data-last version of this function.

@since2.0.0

all
([
const semTask: Effect.Effect<void, never, never>
semTask
,
const semTask: Effect.Effect<void, never, never>
semTask
,
const semTask: Effect.Effect<void, never, never>
semTask
], {
concurrency: "unbounded"
concurrency
: "unbounded"
})
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runFork: <void, never>(effect: Effect.Effect<void, never, never>, options?: RunForkOptions) => RuntimeFiber<void, never>

Runs an effect in the background, returning a fiber that can be observed or interrupted.

Unless you specifically need a Promise or synchronous operation, runFork is a good default choice.

Details

This function is the foundational way to execute an effect in the background. It creates a "fiber," a lightweight, cooperative thread of execution that can be observed (to access its result), interrupted, or joined. Fibers are useful for concurrent programming and allow effects to run independently of the main program flow.

Once the effect is running in a fiber, you can monitor its progress, cancel it if necessary, or retrieve its result when it completes. If the effect fails, the fiber will propagate the failure, which you can observe and handle.

When to Use

Use this function when you need to run an effect in the background, especially if the effect is long-running or performs periodic tasks. It's suitable for tasks that need to run independently but might still need observation or management, like logging, monitoring, or scheduled tasks.

This function is ideal if you don't need the result immediately or if the effect is part of a larger concurrent workflow.

Example (Running an Effect in the Background)

import { Effect, Console, Schedule, Fiber } from "effect"
// ┌─── Effect<number, never, never>
// ▼
const program = Effect.repeat(
Console.log("running..."),
Schedule.spaced("200 millis")
)
// ┌─── RuntimeFiber<number, never>
// ▼
const fiber = Effect.runFork(program)
setTimeout(() => {
Effect.runFork(Fiber.interrupt(fiber))
}, 500)

@since2.0.0

runFork
(
const program: Effect.Effect<void, never, never>
program
)
/*
输出:
timestamp=... level=INFO fiber=#1 message=start elapsed=3ms
timestamp=... level=INFO fiber=#1 message=end elapsed=2010ms
timestamp=... level=INFO fiber=#2 message=start elapsed=2012ms
timestamp=... level=INFO fiber=#2 message=end elapsed=4017ms
timestamp=... level=INFO fiber=#3 message=start elapsed=4018ms
timestamp=... level=INFO fiber=#3 message=end elapsed=6026ms
*/

示例(使用多个许可证控制并发任务执行)

在此示例中,我们创建一个具有五个许可证的信号量,并使用 withPermits(n) 为每个任务分配不同数量的许可证:

import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from "effect"
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<Effect.Semaphore, never, never>> | YieldWrap<Effect.Effect<void[], never, never>>, void>(f: (resume: Effect.Adapter) => Generator<YieldWrap<Effect.Effect<Effect.Semaphore, never, never>> | YieldWrap<Effect.Effect<void[], 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 mutex: Effect.Semaphore
mutex
= yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const makeSemaphore: (permits: number) => Effect.Effect<Effect.Semaphore>

Creates a new semaphore with the specified number of permits.

Details

This function initializes a semaphore that controls concurrent access to a shared resource. The number of permits determines how many tasks can access the resource concurrently.

Example

import { Effect } from "effect"
// Create a semaphore with 3 permits
const mutex = Effect.makeSemaphore(3)

@since2.0.0

makeSemaphore
(5)
const
const tasks: Effect.Effect<void, never, never>[]
tasks
= [1, 2, 3, 4, 5].
Array<number>.map<Effect.Effect<void, never, never>>(callbackfn: (value: number, index: number, array: number[]) => Effect.Effect<void, never, never>, thisArg?: any): Effect.Effect<void, never, never>[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
((
n: number
n
) =>
const mutex: Effect.Semaphore
mutex
.
Semaphore.withPermits(permits: number): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>

Runs an effect with the given number of permits and releases the permits when the effect completes.

Details

This function acquires the specified number of permits before executing the provided effect. Once the effect finishes, the permits are released. If insufficient permits are available, the function will wait until they are released by other tasks.

withPermits
(
n: number
n
)(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const delay: <void, never, never>(self: Effect.Effect<void, never, never>, duration: DurationInput) => Effect.Effect<void, never, never> (+1 overload)

Delays the execution of an effect by a specified Duration.

**Details

This function postpones the execution of the provided effect by the specified duration. The duration can be provided in various formats supported by the Duration module.

Internally, this function does not block the thread; instead, it uses an efficient, non-blocking mechanism to introduce the delay.

Example

import { Console, Effect } from "effect"
const task = Console.log("Task executed")
const program = Console.log("start").pipe(
Effect.andThen(
// Delays the log message by 2 seconds
task.pipe(Effect.delay("2 seconds"))
)
)
Effect.runFork(program)
// Output:
// start
// Task executed

@since2.0.0

delay
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const log: (...message: ReadonlyArray<any>) => Effect.Effect<void, never, never>

Logs one or more messages or error causes at the current log level.

Details

This function provides a simple way to log messages or error causes during the execution of your effects. By default, logs are recorded at the INFO level, but this can be adjusted using other logging utilities (Logger.withMinimumLogLevel). Multiple items, including Cause instances, can be logged in a single call. When logging Cause instances, detailed error information is included in the log output.

The log output includes useful metadata like the current timestamp, log level, and fiber ID, making it suitable for debugging and tracking purposes. This function does not interrupt or alter the effect's execution flow.

Example

import { Cause, Effect } from "effect"
const program = Effect.log(
"message1",
"message2",
Cause.die("Oh no!"),
Cause.die("Oh uh!")
)
Effect.runFork(program)
// Output:
// timestamp=... level=INFO fiber=#0 message=message1 message=message2 cause="Error: Oh no!
// Error: Oh uh!"

@since2.0.0

log
(`process: ${
n: number
n
}`), "2 seconds")
)
.
Pipeable.pipe<Effect.Effect<void, never, never>, Effect.Effect<void, never, never>>(this: Effect.Effect<void, never, never>, ab: (_: Effect.Effect<void, never, never>) => Effect.Effect<void, never, never>): Effect.Effect<void, never, never> (+21 overloads)
pipe
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const withLogSpan: (label: string) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R> (+1 overload)

Adds a log span to an effect for tracking and logging its execution duration.

Details

This function wraps an effect with a log span, providing performance monitoring and debugging capabilities. The log span tracks the duration of the wrapped effect and logs it with the specified label. This is particularly useful when analyzing time-sensitive operations or understanding the execution time of specific tasks in your application.

The logged output will include the label and the total time taken for the operation. The span information is included in the log metadata, making it easy to trace performance metrics in logs.

Example

import { Effect } from "effect"
const program = Effect.gen(function*() {
yield* Effect.sleep("1 second")
yield* Effect.log("The job is finished!")
}).pipe(Effect.withLogSpan("myspan"))
Effect.runFork(program)
// timestamp=... level=INFO fiber=#0 message="The job is finished!" myspan=1011ms

@since2.0.0

withLogSpan
("elapsed"))
)
yield*
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const all: <Effect.Effect<void, never, never>[], {
concurrency: "unbounded";
}>(arg: Effect.Effect<void, never, never>[], options?: {
concurrency: "unbounded";
} | undefined) => Effect.Effect<void[], never, never>

Combines multiple effects into one, returning results based on the input structure.

Details

Use this function when you need to run multiple effects and combine their results into a single output. It supports tuples, iterables, structs, and records, making it flexible for different input types.

For instance, if the input is a tuple:

// ┌─── a tuple of effects
// ▼
Effect.all([effect1, effect2, ...])

the effects are executed sequentially, and the result is a new effect containing the results as a tuple. The results in the tuple match the order of the effects passed to Effect.all.

Concurrency

You can control the execution order (e.g., sequential vs. concurrent) using the concurrency option.

Short-Circuiting Behavior

This function stops execution on the first error it encounters, this is called "short-circuiting". If any effect in the collection fails, the remaining effects will not run, and the error will be propagated. To change this behavior, you can use the mode option, which allows all effects to run and collect results as Either or Option.

The mode option

The { mode: "either" } option changes the behavior of Effect.all to ensure all effects run, even if some fail. Instead of stopping on the first failure, this mode collects both successes and failures, returning an array of Either instances where each result is either a Right (success) or a Left (failure).

Similarly, the { mode: "validate" } option uses Option to indicate success or failure. Each effect returns None for success and Some with the error for failure.

Example (Combining Effects in Tuples)

import { Effect, Console } from "effect"
const tupleOfEffects = [
Effect.succeed(42).pipe(Effect.tap(Console.log)),
Effect.succeed("Hello").pipe(Effect.tap(Console.log))
] as const
// ┌─── Effect<[number, string], never, never>
// ▼
const resultsAsTuple = Effect.all(tupleOfEffects)
Effect.runPromise(resultsAsTuple).then(console.log)
// Output:
// 42
// Hello
// [ 42, 'Hello' ]

Example (Combining Effects in Iterables)

import { Effect, Console } from "effect"
const iterableOfEffects: Iterable<Effect.Effect<number>> = [1, 2, 3].map(
(n) => Effect.succeed(n).pipe(Effect.tap(Console.log))
)
// ┌─── Effect<number[], never, never>
// ▼
const resultsAsArray = Effect.all(iterableOfEffects)
Effect.runPromise(resultsAsArray).then(console.log)
// Output:
// 1
// 2
// 3
// [ 1, 2, 3 ]

Example (Combining Effects in Structs)

import { Effect, Console } from "effect"
const structOfEffects = {
a: Effect.succeed(42).pipe(Effect.tap(Console.log)),
b: Effect.succeed("Hello").pipe(Effect.tap(Console.log))
}
// ┌─── Effect<{ a: number; b: string; }, never, never>
// ▼
const resultsAsStruct = Effect.all(structOfEffects)
Effect.runPromise(resultsAsStruct).then(console.log)
// Output:
// 42
// Hello
// { a: 42, b: 'Hello' }

Example (Combining Effects in Records)

import { Effect, Console } from "effect"
const recordOfEffects: Record<string, Effect.Effect<number>> = {
key1: Effect.succeed(1).pipe(Effect.tap(Console.log)),
key2: Effect.succeed(2).pipe(Effect.tap(Console.log))
}
// ┌─── Effect<{ [x: string]: number; }, never, never>
// ▼
const resultsAsRecord = Effect.all(recordOfEffects)
Effect.runPromise(resultsAsRecord).then(console.log)
// Output:
// 1
// 2
// { key1: 1, key2: 2 }

Example (Short-Circuiting Behavior)

import { Effect, Console } from "effect"
const program = Effect.all([
Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
// Won't execute due to earlier failure
Effect.succeed("Task3").pipe(Effect.tap(Console.log))
])
Effect.runPromiseExit(program).then(console.log)
// Output:
// Task1
// {
// _id: 'Exit',
// _tag: 'Failure',
// cause: { _id: 'Cause', _tag: 'Fail', failure: 'Task2: Oh no!' }
// }

Example (Collecting Results with mode: "either")

import { Effect, Console } from "effect"
const effects = [
Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
Effect.succeed("Task3").pipe(Effect.tap(Console.log))
]
const program = Effect.all(effects, { mode: "either" })
Effect.runPromiseExit(program).then(console.log)
// Output:
// Task1
// Task3
// {
// _id: 'Exit',
// _tag: 'Success',
// value: [
// { _id: 'Either', _tag: 'Right', right: 'Task1' },
// { _id: 'Either', _tag: 'Left', left: 'Task2: Oh no!' },
// { _id: 'Either', _tag: 'Right', right: 'Task3' }
// ]
// }

Example (Collecting Results with mode: "validate")

import { Effect, Console } from "effect"
const effects = [
Effect.succeed("Task1").pipe(Effect.tap(Console.log)),
Effect.fail("Task2: Oh no!").pipe(Effect.tap(Console.log)),
Effect.succeed("Task3").pipe(Effect.tap(Console.log))
]
const program = Effect.all(effects, { mode: "validate" })
Effect.runPromiseExit(program).then((result) => console.log("%o", result))
// Output:
// Task1
// Task3
// {
// _id: 'Exit',
// _tag: 'Failure',
// cause: {
// _id: 'Cause',
// _tag: 'Fail',
// failure: [
// { _id: 'Option', _tag: 'None' },
// { _id: 'Option', _tag: 'Some', value: 'Task2: Oh no!' },
// { _id: 'Option', _tag: 'None' }
// ]
// }
// }

@seeforEach for iterating over elements and applying an effect.

@seeallWith for a data-last version of this function.

@since2.0.0

all
(
const tasks: Effect.Effect<void, never, never>[]
tasks
, {
concurrency: "unbounded"
concurrency
: "unbounded" })
})
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runFork: <void, never>(effect: Effect.Effect<void, never, never>, options?: RunForkOptions) => RuntimeFiber<void, never>

Runs an effect in the background, returning a fiber that can be observed or interrupted.

Unless you specifically need a Promise or synchronous operation, runFork is a good default choice.

Details

This function is the foundational way to execute an effect in the background. It creates a "fiber," a lightweight, cooperative thread of execution that can be observed (to access its result), interrupted, or joined. Fibers are useful for concurrent programming and allow effects to run independently of the main program flow.

Once the effect is running in a fiber, you can monitor its progress, cancel it if necessary, or retrieve its result when it completes. If the effect fails, the fiber will propagate the failure, which you can observe and handle.

When to Use

Use this function when you need to run an effect in the background, especially if the effect is long-running or performs periodic tasks. It's suitable for tasks that need to run independently but might still need observation or management, like logging, monitoring, or scheduled tasks.

This function is ideal if you don't need the result immediately or if the effect is part of a larger concurrent workflow.

Example (Running an Effect in the Background)

import { Effect, Console, Schedule, Fiber } from "effect"
// ┌─── Effect<number, never, never>
// ▼
const program = Effect.repeat(
Console.log("running..."),
Schedule.spaced("200 millis")
)
// ┌─── RuntimeFiber<number, never>
// ▼
const fiber = Effect.runFork(program)
setTimeout(() => {
Effect.runFork(Fiber.interrupt(fiber))
}, 500)

@since2.0.0

runFork
(
const program: Effect.Effect<void, never, never>
program
)
/*
输出:
timestamp=... level=INFO fiber=#1 message="process: 1" elapsed=2011ms
timestamp=... level=INFO fiber=#2 message="process: 2" elapsed=2017ms
timestamp=... level=INFO fiber=#3 message="process: 3" elapsed=4020ms
timestamp=... level=INFO fiber=#4 message="process: 4" elapsed=6025ms
timestamp=... level=INFO fiber=#5 message="process: 5" elapsed=8034ms
*/