Skip to content

调度组合器

调度定义有状态的、可能有副作用的重复事件调度,并以多种方式组合。组合器允许我们获取调度并将它们组合在一起以获得其他调度。

为了演示不同调度的功能,我们将使用以下辅助函数, 它记录每次重复以及相应的延迟(以毫秒为单位),格式为:

#<重复次数>: <延迟毫秒数>

辅助函数(记录执行延迟)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10 // 限制执行次数
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..." // 如果有更多执行,表示截断
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)" // 标记最后一次执行
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}

调度可以以不同方式组合:

模式描述
联合组合两个调度,如果任一调度想要继续则重复,使用较短的延迟。
交集组合两个调度,仅当两个调度都想要继续时才重复,使用较长的延迟。
序列通过完全运行第一个调度,然后切换到第二个来组合两个调度。

组合两个调度,如果任一调度想要继续则重复,使用较短的延迟。

示例(组合指数和间隔调度)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<[Duration.Duration, number], unknown, never>
schedule
=
import Schedule
Schedule
.
const union: <Duration.Duration, unknown, never, number, unknown, never>(self: Schedule.Schedule<Duration.Duration, unknown, never>, that: Schedule.Schedule<number, unknown, never>) => Schedule.Schedule<[Duration.Duration, number], unknown, never> (+1 overload)

Combines two schedules, continuing execution as long as at least one of them allows it, using the shorter delay.

Details

This function combines two schedules into a single schedule that executes in parallel. If either schedule allows continuation, the merged schedule continues. When both schedules produce delays, the schedule selects the shorter delay to determine the next step.

The output of the new schedule is a tuple containing the outputs of both schedules. The input type is the intersection of both schedules' input types.

This is useful for scenarios where multiple scheduling conditions should be considered, ensuring execution proceeds if at least one schedule permits it.

@seeunionWith If you need to use a custom merge function.

@since2.0.0

union
(
import Schedule
Schedule
.
const exponential: (base: Duration.DurationInput, factor?: number) => Schedule.Schedule<Duration.Duration>

Creates a schedule that recurs indefinitely with exponentially increasing delays.

Details

This schedule starts with an initial delay of base and increases the delay exponentially on each repetition using the formula base * factor^n, where n is the number of times the schedule has executed so far. If no factor is provided, it defaults to 2, causing the delay to double after each execution.

@since2.0.0

exponential
("100 millis"),
import Schedule
Schedule
.
const spaced: (duration: 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
("1 second")
)
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<[Duration.Duration, number], unknown, never>
schedule
)
/*
输出:
#1: 100ms < exponential
#2: 200ms
#3: 400ms
#4: 800ms
#5: 1000ms < spaced
#6: 1000ms
#7: 1000ms
#8: 1000ms
#9: 1000ms
#10: 1000ms
...
*/

Schedule.union 操作符在每一步选择最短的延迟,所以当组合指数调度和间隔调度时,初始重复将遵循指数退避,然后一旦延迟超过该值就稳定为间隔调度。

组合两个调度,仅当两个调度都想要继续时才重复,使用较长的延迟。

示例(用固定重试次数限制指数退避)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<[Duration.Duration, number], unknown, never>
schedule
=
import Schedule
Schedule
.
const intersect: <Duration.Duration, unknown, never, number, unknown, never>(self: Schedule.Schedule<Duration.Duration, unknown, never>, that: Schedule.Schedule<number, unknown, never>) => Schedule.Schedule<[Duration.Duration, number], unknown, never> (+1 overload)

Combines two schedules, continuing only if both schedules want to continue, using the longer delay.

Details

This function takes two schedules and creates a new schedule that only continues execution if both schedules allow it. The interval between recurrences is determined by the longer delay between the two schedules.

The output of the resulting schedule is a tuple containing the outputs of both schedules. The input type is the intersection of both schedules' input types.

This is useful when coordinating multiple scheduling conditions where execution should proceed only when both schedules permit it.

@seeintersectWith If you need to use a custom merge function.

@since2.0.0

intersect
(
import Schedule
Schedule
.
const exponential: (base: Duration.DurationInput, factor?: number) => Schedule.Schedule<Duration.Duration>

Creates a schedule that recurs indefinitely with exponentially increasing delays.

Details

This schedule starts with an initial delay of base and increases the delay exponentially on each repetition using the formula base * factor^n, where n is the number of times the schedule has executed so far. If no factor is provided, it defaults to 2, causing the delay to double after each execution.

@since2.0.0

exponential
("10 millis"),
import Schedule
Schedule
.
const recurs: (n: number) => Schedule.Schedule<number>

A schedule that recurs a fixed number of times before terminating.

Details

This schedule will continue executing until it has been stepped n times, after which it will stop. The output of the schedule is the current count of recurrences.

@since2.0.0

recurs
(5)
)
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<[Duration.Duration, number], unknown, never>
schedule
)
/*
输出:
#1: 10ms < exponential
#2: 20ms
#3: 40ms
#4: 80ms
#5: 160ms
(end) < recurs
*/

Schedule.intersect 操作符强制执行两个调度的约束。在此示例中,调度遵循指数退避,但由于 Schedule.recurs(5) 限制在5次重复后停止。

通过完全运行第一个调度,然后切换到第二个来组合两个调度。

示例(从固定重试切换到周期性执行)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<number, unknown, never>
schedule
=
import Schedule
Schedule
.
const andThen: <number, unknown, never, number, unknown, never>(self: Schedule.Schedule<number, unknown, never>, that: Schedule.Schedule<number, unknown, never>) => Schedule.Schedule<number, unknown, never> (+1 overload)

Runs two schedules sequentially, merging their outputs.

Details

This function executes two schedules one after the other. The first schedule runs to completion, and then the second schedule begins execution. Unlike

andThenEither

, this function merges the outputs instead of wrapping them in Either, allowing both schedules to contribute their results directly.

This is useful when a workflow consists of two phases where the second phase should start only after the first one has fully completed.

@seeandThenEither If you need to keep track of which schedule produced each result.

@since2.0.0

andThen
(
import Schedule
Schedule
.
const recurs: (n: number) => Schedule.Schedule<number>

A schedule that recurs a fixed number of times before terminating.

Details

This schedule will continue executing until it has been stepped n times, after which it will stop. The output of the schedule is the current count of recurrences.

@since2.0.0

recurs
(5),
import Schedule
Schedule
.
const spaced: (duration: 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
("1 second")
)
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<number, unknown, never>
schedule
)
/*
输出:
#1: 0ms < recurs
#2: 0ms
#3: 0ms
#4: 0ms
#5: 0ms
#6: 1000ms < spaced
#7: 1000ms
#8: 1000ms
#9: 1000ms
#10: 1000ms
...
*/

第一个调度运行直到完成,之后第二个调度接管。在此示例中,副作用最初执行5次且无延迟,然后每1秒继续一次。

Schedule.jittered 组合器通过在指定范围内应用随机延迟来修改调度。

当资源由于过载或争用而停止服务时,重试和退避对我们没有帮助。如果所有失败的API调用都退避到同一时间点,它们会造成另一次过载或争用。抖动为调度的延迟添加一定量的随机性。这有助于我们避免意外同步并意外使服务停机。

研究 表明 Schedule.jittered(0.0, 1.0) 是在重试中引入随机性的有效方法。

示例(抖动指数退避)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<Duration.Duration, unknown, never>
schedule
=
import Schedule
Schedule
.
const jittered: <Duration.Duration, unknown, never>(self: Schedule.Schedule<Duration.Duration, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Returns a new schedule that randomly adjusts the interval size within a range.

Details

This function modifies a schedule so that its delay between executions is randomly varied within a range. By default, the delay is adjusted between 80% (0.8 * interval) and 120% (1.2 * interval) of the original interval size.

This is useful for adding randomness to repeated executions, reducing contention in distributed systems, and avoiding synchronized execution patterns that can cause bottlenecks.

@seejitteredWith If you need to specify custom min/max values.

@since2.0.0

jittered
(
import Schedule
Schedule
.
const exponential: (base: Duration.DurationInput, factor?: number) => Schedule.Schedule<Duration.Duration>

Creates a schedule that recurs indefinitely with exponentially increasing delays.

Details

This schedule starts with an initial delay of base and increases the delay exponentially on each repetition using the formula base * factor^n, where n is the number of times the schedule has executed so far. If no factor is provided, it defaults to 2, causing the delay to double after each execution.

@since2.0.0

exponential
("10 millis"))
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<Duration.Duration, unknown, never>
schedule
)
/*
输出:
#1: 10.448486ms
#2: 21.134521ms
#3: 47.245117ms
#4: 88.263184ms
#5: 163.651367ms
#6: 335.818848ms
#7: 719.126709ms
#8: 1266.18457ms
#9: 2931.252441ms
#10: 6121.593018ms
...
*/

Schedule.jittered 组合器在范围内为延迟引入随机性。例如,对指数退避应用抖动确保每次重试在稍微不同的时间发生,减少压垮系统的风险。

你可以使用 Schedule.whileInputSchedule.whileOutput 根据应用于其输入或输出的条件来限制调度继续的时间。

示例(基于输出停止)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<number, unknown, never>
schedule
=
import Schedule
Schedule
.
const whileOutput: <number, unknown, never>(self: Schedule.Schedule<number, unknown, never>, f: Predicate<number>) => Schedule.Schedule<number, unknown, never> (+1 overload)

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

Details

This function modifies an existing schedule so that it only continues execution while a provided condition holds true for its output. If the predicate returns false, the schedule stops.

@seewhileOutputEffect If you need to use an effectful predicate.

@since2.0.0

whileOutput
(
import Schedule
Schedule
.
const recurs: (n: number) => Schedule.Schedule<number>

A schedule that recurs a fixed number of times before terminating.

Details

This schedule will continue executing until it has been stepped n times, after which it will stop. The output of the schedule is the current count of recurrences.

@since2.0.0

recurs
(5), (
n: number
n
) =>
n: number
n
<= 2)
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<number, unknown, never>
schedule
)
/*
输出:
#1: 0ms < recurs
#2: 0ms
#3: 0ms
(end) < whileOutput
*/

Schedule.whileOutput 根据调度的输出过滤重复。在此示例中,一旦输出超过 2,调度就会停止,即使 Schedule.recurs(5) 允许最多5次重复。

Schedule.modifyDelay 组合器允许你根据重复次数或其他输出条件动态更改调度的延迟。

示例(在一定重复次数后减少延迟)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<number, unknown, never>
schedule
=
import Schedule
Schedule
.
const modifyDelay: <number, unknown, never>(self: Schedule.Schedule<number, unknown, never>, f: (out: number, duration: Duration.Duration) => Duration.DurationInput) => Schedule.Schedule<number, unknown, never> (+1 overload)

Returns a new schedule that modifies the delay between executions using a custom function.

Details

This function transforms an existing schedule by applying f to modify the delay before each execution. The function receives both the schedule's output (out) and the originally computed delay (duration), and returns a new adjusted delay.

@seemodifyDelayEffect If you need to use an effectful function.

@since2.0.0

modifyDelay
(
import Schedule
Schedule
.
const spaced: (duration: 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
("1 second"),
(
out: number
out
,
duration: Duration.Duration
duration
) => (
out: number
out
> 2 ? "100 millis" :
duration: Duration.Duration
duration
)
)
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<number, unknown, never>
schedule
)
/*
输出:
#1: 1000ms
#2: 1000ms
#3: 1000ms
#4: 100ms < modifyDelay
#5: 100ms
#6: 100ms
#7: 100ms
#8: 100ms
#9: 100ms
#10: 100ms
...
*/

延迟修改在执行期间动态应用。在此示例中,前三次重复遵循原始的 1秒 间隔。之后,延迟降至 100毫秒,使后续重复更频繁地发生。

Schedule.tapInputSchedule.tapOutput 允许你对调度的输入或输出执行额外的有副作用操作,而不修改其行为。

示例(记录调度输出)

import {
import Array
Array
,
import Chunk
Chunk
,
import Duration
Duration
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
,
import Schedule
Schedule
,
import Console
Console
} from "effect"
24 collapsed lines
const
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
= (
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
:
import Schedule
Schedule
.
interface Schedule<out Out, in In = unknown, out R = never>

A Schedule<Out, In, R> defines a recurring schedule, which consumes values of type In, and which returns values of type Out.

The Schedule type is structured as follows:

// ┌─── The type of output produced by the schedule
// │ ┌─── The type of input consumed by the schedule
// │ │ ┌─── Additional requirements for the schedule
// ▼ ▼ ▼
Schedule<Out, In, Requirements>

A schedule operates by consuming values of type In (such as errors in the case of Effect.retry, or values in the case of Effect.repeat) and producing values of type Out. It determines when to halt or continue the execution based on input values and its internal state.

The inclusion of a Requirements parameter allows the schedule to leverage additional services or resources as needed.

Schedules are defined as a possibly infinite set of intervals spread out over time. Each interval defines a window in which recurrence is possible.

When schedules are used to repeat or retry effects, the starting boundary of each interval produced by a schedule is used as the moment when the effect will be executed again.

Schedules can be composed in different ways:

  • Union: Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.
  • Intersection: Combines two schedules and recurs only if both schedules want to continue, using the longer delay.
  • Sequencing: Combines two schedules by running the first one fully, then switching to the second.

In addition, schedule inputs and outputs can be transformed, filtered (to terminate a schedule early in response to some input or output), and so forth.

A variety of other operators exist for transforming and combining schedules, and the companion object for Schedule contains all common types of schedules, both for performing retrying, as well as performing repetition.

@since2.0.0

@since2.0.0

Schedule
<unknown>,
delay: Duration.DurationInput
delay
:
import Duration
Duration
.
type DurationInput = number | bigint | Duration.Duration | readonly [seconds: number, nanos: number] | `${number} nano` | `${number} nanos` | `${number} micro` | `${number} micros` | `${number} milli` | `${number} millis` | `${number} second` | `${number} seconds` | `${number} minute` | `${number} minutes` | `${number} hour` | `${number} hours` | `${number} day` | `${number} days` | `${number} week` | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10
const
const delays: Duration.Duration[]
delays
=
import Chunk
Chunk
.
const toArray: <Chunk.Chunk<Duration.Duration>>(self: Chunk.Chunk<Duration.Duration>) => Duration.Duration[]

Converts a Chunk into an Array. If the provided Chunk is non-empty (NonEmptyChunk), the function will return a NonEmptyArray, ensuring the non-empty property is preserved.

@since2.0.0

toArray
(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const runSync: <Chunk.Chunk<Duration.Duration>, never>(effect: Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never>) => Chunk.Chunk<Duration.Duration>

Executes an effect synchronously, running it immediately and returning the result.

Details

This function evaluates the provided effect synchronously, returning its result directly. It is ideal for effects that do not fail or include asynchronous operations. If the effect does fail or involves async tasks, it will throw an error. Execution stops at the point of failure or asynchronous operation, making it unsuitable for effects that require asynchronous handling.

Important: Attempting to run effects that involve asynchronous operations or failures will result in exceptions being thrown, so use this function with care for purely synchronous and error-free effects.

When to Use

Use this function when:

  • You are sure that the effect will not fail or involve asynchronous operations.
  • You need a direct, synchronous result from the effect.
  • You are working within a context where asynchronous effects are not allowed.

Avoid using this function for effects that can fail or require asynchronous handling. For such cases, consider using

runPromise

or

runSyncExit

.

Example (Synchronous Logging)

import { Effect } from "effect"
const program = Effect.sync(() => {
console.log("Hello, World!")
return 1
})
const result = Effect.runSync(program)
// Output: Hello, World!
console.log(result)
// Output: 1

Example (Incorrect Usage with Failing or Async Effects)

import { Effect } from "effect"
try {
// Attempt to run an effect that fails
Effect.runSync(Effect.fail("my error"))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) Error: my error
try {
// Attempt to run an effect that involves async work
Effect.runSync(Effect.promise(() => Promise.resolve(1)))
} catch (e) {
console.error(e)
}
// Output:
// (FiberFailure) AsyncFiberException: Fiber #0 cannot be resolved synchronously. This is caused by using runSync on an effect that performs async work

@seerunSyncExit for a version that returns an Exit type instead of throwing an error.

@since2.0.0

runSync
(
import Schedule
Schedule
.
const run: <Duration.Duration, number, never>(self: Schedule.Schedule<Duration.Duration, number, never>, now: number, input: Iterable<number>) => Effect.Effect<Chunk.Chunk<Duration.Duration>, never, never> (+1 overload)

Runs a schedule using the provided inputs and collects all outputs.

Details

This function executes a given schedule with a sequence of input values and accumulates all outputs into a Chunk. The schedule starts execution at the specified now timestamp and proceeds according to its defined behavior.

This is useful for batch processing, simulating execution, or testing schedules with predefined input sequences.

@since2.0.0

run
(
import Schedule
Schedule
.
const delays: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>) => Schedule.Schedule<Duration.Duration, unknown, never>

Transforms a schedule to output the delay between each occurrence.

Details

This function modifies an existing schedule so that instead of producing its original output, it now returns the delay between each scheduled execution.

@since2.0.0

delays
(
import Schedule
Schedule
.
const addDelay: <unknown, unknown, never>(self: Schedule.Schedule<unknown, unknown, never>, f: (out: unknown) => Duration.DurationInput) => Schedule.Schedule<unknown, unknown, never> (+1 overload)

Adds a delay to every interval in a schedule.

Details

This function modifies a given schedule by applying an additional delay to every interval it defines. The delay is determined by the provided function, which takes the schedule's output and returns a delay duration.

@seeaddDelayEffect If you need to compute the delay using an effectful function.

@since2.0.0

addDelay
(
schedule: Schedule.Schedule<unknown, unknown, never>
schedule
, () =>
delay: Duration.DurationInput
delay
)),
var Date: DateConstructor

Enables basic storage and retrieval of dates and times.

Date
.
DateConstructor.now(): number

Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).

now
(),
import Array
Array
.
const range: (start: number, end: number) => Array.NonEmptyArray<number>

Return a NonEmptyArray containing a range of integers, including both endpoints.

Example

import { range } from "effect/Array"
const result = range(1, 3)
console.log(result) // [1, 2, 3]

@since2.0.0

range
(0,
const maxRecurs: 10
maxRecurs
)
)
)
)
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.forEach(callbackfn: (value: Duration.Duration, index: number, array: Duration.Duration[]) => void, thisArg?: any): void

Performs the specified action for each element in an array.

@paramcallbackfn A function that accepts up to three arguments. forEach 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.

forEach
((
duration: Duration.Duration
duration
,
i: number
i
) => {
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
.
globalThis.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
(
i: number
i
===
const maxRecurs: 10
maxRecurs
? "..."
:
i: number
i
===
const delays: Duration.Duration[]
delays
.
globalThis.Array<Duration>.length: number

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

length
- 1
? "(end)"
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

toMillis
(
duration: Duration.Duration
duration
)}ms`
)
})
}
const
const schedule: Schedule.Schedule<number, unknown, never>
schedule
=
import Schedule
Schedule
.
const tapOutput: <number, unknown, never, void, never>(self: Schedule.Schedule<number, unknown, never>, f: (out: number) => Effect.Effect<void, never, never>) => Schedule.Schedule<number, unknown, never> (+1 overload)

Returns a new schedule that runs the given effectful function for each output before continuing execution.

Details

This function allows side effects to be performed on each output produced by the schedule. It does not modify the schedule’s behavior but ensures that the provided function f runs after each step.

@since2.0.0

tapOutput
(
import Schedule
Schedule
.
const recurs: (n: number) => Schedule.Schedule<number>

A schedule that recurs a fixed number of times before terminating.

Details

This schedule will continue executing until it has been stepped n times, after which it will stop. The output of the schedule is the current count of recurrences.

@since2.0.0

recurs
(2), (
n: number
n
) =>
import Console
Console
.
const log: (...args: ReadonlyArray<any>) => Effect.Effect<void>

@since2.0.0

log
(`Schedule Output: ${
n: number
n
}`)
)
const log: (schedule: Schedule.Schedule<unknown>, delay?: Duration.DurationInput) => void
log
(
const schedule: Schedule.Schedule<number, unknown, never>
schedule
)
/*
输出:
Schedule Output: 0
Schedule Output: 1
Schedule Output: 2
#1: 0ms
#2: 0ms
(end)
*/

Schedule.tapOutput 在每次重复之前运行一个副作用,使用调度的当前输出作为输入。这对于日志记录、调试或触发副作用很有用。