Skip to content

Schedule Combinators

Schedules define stateful, possibly effectful, recurring schedules of events, and compose in a variety of ways. Combinators allow us to take schedules and combine them together to get other schedules.

To demonstrate the functionality of different schedules, we will use the following helper function that logs each repetition along with the corresponding delay in milliseconds, formatted as:

#<repetition>: <delay in ms>

Helper (Logging Execution Delays)

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` | ... 5 more ... | `${number} weeks`

@since2.0.0

DurationInput
= 0
): void => {
const
const maxRecurs: 10
maxRecurs
= 10 // Limit the number of executions
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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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
? "..." // Indicate truncation if there are more executions
:
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)" // Mark the last execution
: `#${
i: number
i
+ 1}: ${
import Duration
Duration
.
const toMillis: (self: Duration.DurationInput) => number

@since2.0.0

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

Schedules can be composed in different ways:

ModeDescription
UnionCombines two schedules and recurs if either schedule wants to continue, using the shorter delay.
IntersectionCombines two schedules and recurs only if both schedules want to continue, using the longer delay.
SequencingCombines two schedules by running the first one fully, then switching to the second.

Combines two schedules and recurs if either schedule wants to continue, using the shorter delay.

Example (Combining Exponential and Spaced Intervals)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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<...> (+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
)
/*
Output:
#1: 100ms < exponential
#2: 200ms
#3: 400ms
#4: 800ms
#5: 1000ms < spaced
#6: 1000ms
#7: 1000ms
#8: 1000ms
#9: 1000ms
#10: 1000ms
...
*/

The Schedule.union operator selects the shortest delay at each step, so when combining an exponential schedule with a spaced interval, the initial recurrences will follow the exponential backoff, then settle into the spaced interval once the delays exceed that value.

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

Example (Limiting Exponential Backoff with a Fixed Number of Retries)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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<...> (+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
)
/*
Output:
#1: 10ms < exponential
#2: 20ms
#3: 40ms
#4: 80ms
#5: 160ms
(end) < recurs
*/

The Schedule.intersect operator enforces both schedules’ constraints. In this example, the schedule follows an exponential backoff but stops after 5 recurrences due to the Schedule.recurs(5) limit.

Combines two schedules by running the first one fully, then switching to the second.

Example (Switching from Fixed Retries to Periodic Execution)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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
)
/*
Output:
#1: 0ms < recurs
#2: 0ms
#3: 0ms
#4: 0ms
#5: 0ms
#6: 1000ms < spaced
#7: 1000ms
#8: 1000ms
#9: 1000ms
#10: 1000ms
...
*/

The first schedule runs until completion, after which the second schedule takes over. In this example, the effect initially executes 5 times with no delay, then continues every 1 second.

The Schedule.jittered combinator modifies a schedule by applying a random delay within a specified range.

When a resource is out of service due to overload or contention, retrying and backing off doesn’t help us. If all failed API calls are backed off to the same point of time, they cause another overload or contention. Jitter adds some amount of randomness to the delay of the schedule. This helps us to avoid ending up accidentally synchronizing and taking the service down by accident.

Research suggests that Schedule.jittered(0.0, 1.0) is an effective way to introduce randomness in retries.

Example (Jittered Exponential Backoff)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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
)
/*
Output:
#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
...
*/

The Schedule.jittered combinator introduces randomness to delays within a range. For example, applying jitter to an exponential backoff ensures that each retry occurs at a slightly different time, reducing the risk of overwhelming the system.

You can use Schedule.whileInput or Schedule.whileOutput to limit how long a schedule continues based on conditions applied to its input or output.

Example (Stopping Based on Output)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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
)
/*
Output:
#1: 0ms < recurs
#2: 0ms
#3: 0ms
(end) < whileOutput
*/

Schedule.whileOutput filters repetitions based on the output of the schedule. In this example, the schedule stops once the output exceeds 2, even though Schedule.recurs(5) allows up to 5 repetitions.

The Schedule.modifyDelay combinator allows you to dynamically change the delay of a schedule based on the number of repetitions or other output conditions.

Example (Reducing Delay After a Certain Number of Repetitions)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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
)
/*
Output:
#1: 1000ms
#2: 1000ms
#3: 1000ms
#4: 100ms < modifyDelay
#5: 100ms
#6: 100ms
#7: 100ms
#8: 100ms
#9: 100ms
#10: 100ms
...
*/

The delay modification applies dynamically during execution. In this example, the first three repetitions follow the original 1-second spacing. After that, the delay drops to 100 milliseconds, making subsequent repetitions occur more frequently.

Schedule.tapInput and Schedule.tapOutput allow you to perform additional effectful operations on a schedule’s input or output without modifying its behavior.

Example (Logging Schedule Outputs)

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` | ... 5 more ... | `${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

.

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

@example

// Title: 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

// Title: 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

@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<...> (+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"
assert.deepStrictEqual(range(1, 3), [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
)
/*
Output:
Schedule Output: 0
Schedule Output: 1
Schedule Output: 2
#1: 0ms
#2: 0ms
(end)
*/

Schedule.tapOutput runs an effect before each recurrence, using the schedule’s current output as input. This can be useful for logging, debugging, or triggering side effects.