A semaphore is a synchronization mechanism used to manage access to a shared resource. In Effect, semaphores help control resource access or coordinate tasks within asynchronous, concurrent operations.
A semaphore acts as a generalized mutex, allowing a set number of permits to be held and released concurrently. Permits act like tickets, giving tasks or fibers controlled access to a shared resource. When no permits are available, tasks trying to acquire one will wait until a permit is released.
Creating a Semaphore
The Effect.makeSemaphore function initializes a semaphore with a specified number of permits.
Each permit allows one task to access a resource or perform an operation concurrently, and multiple permits enable a configurable level of concurrency.
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
constmutex= Effect.makeSemaphore(3)
@since ― 2.0.0
makeSemaphore(3)
withPermits
The withPermits method lets you specify the number of permits required to run an effect. Once the specified permits are available, it runs the effect, automatically releasing the permits when the task completes.
Example (Forcing Sequential Task Execution with a One-Permit Semaphore)
In this example, three tasks are started concurrently, but they run sequentially because the one-permit semaphore only allows one task to proceed at a time.
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.
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.
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"
constprogram= Effect.gen(function*() {
console.log("Starting task...")
yield* Effect.sleep("3 seconds") // Waits for 3 seconds
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.
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.
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
constmutex= Effect.makeSemaphore(3)
@since ― 2.0.0
makeSemaphore(1)
11
12
// Wrap the task to require one permit, forcing sequential execution
13
const
constsemTask:Effect.Effect<void, never, never>
semTask=
constmutex:Effect.Semaphore
mutex
14
.
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.
constwithLogSpan: (label:string) => <A, E, R>(effect:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, R> (+1overload)
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"
constprogram= 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
@since ― 2.0.0
withLogSpan("elapsed"))
16
17
// Run 3 tasks concurrently, but they execute sequentially
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.
@see ― forEach for iterating over elements and applying an effect.
@see ― allWith for a data-last version of this function.
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.
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.
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.
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@param ― thisArg 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) =>
7
constmutex:Effect.Semaphore
mutex
8
.
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.
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.
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.
constwithLogSpan: (label:string) => <A, E, R>(effect:Effect.Effect<A, E, R>) =>Effect.Effect<A, E, R> (+1overload)
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"
constprogram= 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
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.
@see ― forEach for iterating over elements and applying an effect.
@see ― allWith for a data-last version of this function.
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.