<
const Schedules extends NonEmptyReadonlyArray<
Schedule<any, any, any, any>
>
>(
schedules: Schedules
): Schedule<
Duration.Duration,
UnionToIntersection<Input<Schedules[number]>>,
Error<Schedules[number]>,
Env<Schedules[number]>
>Combines schedules by recurring while at least one schedule wants to recur, using the minimum delay between recurrences and outputting that minimum delay.
When to use
Use when a combined policy should continue while any schedule still recurs, and should wait for the fastest schedule between recurrences.
Example (Combining retry schedules by their minimum delay)
import { Console, Data, Effect, Schedule } from "effect"
class RetryAttemptError extends Data.TaggedError("RetryAttemptError")<{ readonly message: string }> {}
const retrySchedule = Schedule.min([
Schedule.fixed("5 seconds"),
Schedule.exponential("5 seconds"),
Schedule.spaced("10 seconds")
])
const program = Effect.gen(function*() {
let attempt = 0
yield* Effect.retry(
Effect.gen(function*() {
attempt++
yield* Console.log(`Retry attempt ${attempt}`)
if (attempt < 3) {
return yield* Effect.fail(new RetryAttemptError({ message: `Attempt ${attempt} failed` }))
}
return "success"
}),
retrySchedule.pipe(
Schedule.tap(({ output: duration }) =>
Console.log(`Waiting for the fastest schedule: ${duration}`)
)
)
)
})export const const min: <
Schedules extends NonEmptyReadonlyArray<
Schedule<any, any, any, any>
>
>(
schedules: Schedules
) => Schedule<
Duration.Duration,
UnionToIntersection<Input<Schedules[number]>>,
Error<Schedules[number]>,
Env<Schedules[number]>
>
Combines schedules by recurring while at least one schedule wants to recur,
using the minimum delay between recurrences and outputting that minimum delay.
When to use
Use when a combined policy should continue while any schedule still recurs,
and should wait for the fastest schedule between recurrences.
Example (Combining retry schedules by their minimum delay)
import { Console, Data, Effect, Schedule } from "effect"
class RetryAttemptError extends Data.TaggedError("RetryAttemptError")<{ readonly message: string }> {}
const retrySchedule = Schedule.min([
Schedule.fixed("5 seconds"),
Schedule.exponential("5 seconds"),
Schedule.spaced("10 seconds")
])
const program = Effect.gen(function*() {
let attempt = 0
yield* Effect.retry(
Effect.gen(function*() {
attempt++
yield* Console.log(`Retry attempt ${attempt}`)
if (attempt < 3) {
return yield* Effect.fail(new RetryAttemptError({ message: `Attempt ${attempt} failed` }))
}
return "success"
}),
retrySchedule.pipe(
Schedule.tap(({ output: duration }) =>
Console.log(`Waiting for the fastest schedule: ${duration}`)
)
)
)
})
min = <
const function (type parameter) Schedules in <const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>>(schedules: Schedules): Schedule<Duration.Duration, UnionToIntersection<Input<Schedules[number]>>, Error<Schedules[number]>, Env<Schedules[number]>>Schedules extends import NonEmptyReadonlyArrayNonEmptyReadonlyArray<
interface Schedule<out Output, in Input = unknown, out Error = never, out Env = never>A Schedule defines a strategy for repeating or retrying effects based on some policy.
Example (Defining retry and repeat schedules)
import { Console, Data, Effect, Schedule } from "effect"
class NetworkError extends Data.TaggedError("NetworkError")<{
readonly attempt: number
}> {}
// Basic retry schedule - retry up to 3 times with exponential backoff
const retrySchedule = Schedule.max([
Schedule.exponential("100 millis"),
Schedule.recurs(3)
])
// Basic repeat schedule - repeat every 30 seconds forever
const repeatSchedule: Schedule.Schedule<number, unknown, never> = Schedule
.spaced("30 seconds")
const program = Effect.gen(function*() {
let attempts = 0
const result1 = yield* Effect.retry(
Effect.gen(function*() {
attempts++
if (attempts < 3) {
return yield* Effect.fail(new NetworkError({ attempt: attempts }))
}
return "Success"
}),
retrySchedule
)
console.log(result1) // "Success"
yield* Console.log("heartbeat").pipe(
Effect.repeat(repeatSchedule.pipe(Schedule.upTo({ times: 5 })))
)
})
The Schedule namespace contains types and utilities for working with schedules.
Schedule<any, any, any, any>
>
>(
schedules: const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>schedules: function (type parameter) Schedules in <const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>>(schedules: Schedules): Schedule<Duration.Duration, UnionToIntersection<Input<Schedules[number]>>, Error<Schedules[number]>, Env<Schedules[number]>>Schedules
): interface Schedule<out Output, in Input = unknown, out Error = never, out Env = never>A Schedule defines a strategy for repeating or retrying effects based on some policy.
Example (Defining retry and repeat schedules)
import { Console, Data, Effect, Schedule } from "effect"
class NetworkError extends Data.TaggedError("NetworkError")<{
readonly attempt: number
}> {}
// Basic retry schedule - retry up to 3 times with exponential backoff
const retrySchedule = Schedule.max([
Schedule.exponential("100 millis"),
Schedule.recurs(3)
])
// Basic repeat schedule - repeat every 30 seconds forever
const repeatSchedule: Schedule.Schedule<number, unknown, never> = Schedule
.spaced("30 seconds")
const program = Effect.gen(function*() {
let attempts = 0
const result1 = yield* Effect.retry(
Effect.gen(function*() {
attempts++
if (attempts < 3) {
return yield* Effect.fail(new NetworkError({ attempt: attempts }))
}
return "Success"
}),
retrySchedule
)
console.log(result1) // "Success"
yield* Console.log("heartbeat").pipe(
Effect.repeat(repeatSchedule.pipe(Schedule.upTo({ times: 5 })))
)
})
The Schedule namespace contains types and utilities for working with schedules.
Schedule<
import DurationDuration.type Duration.Duration = /*unresolved*/ anyDuration,
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : neverTransforms a union type into an intersection type.
When to use
Use to combine all members of a union into a single type with all their
properties. This is useful in advanced generic code where you need to merge
union variants.
Details
- Uses distributive conditional types and contra-variant inference.
- If the union members are incompatible (e.g.
string | number), the
result is never.
Example (Converting a union to an intersection)
import type { Types } from "effect"
type Union = { a: string } | { b: number }
type Result = Types.UnionToIntersection<Union>
// { a: string } & { b: number }
UnionToIntersection<
type Input<S> = S extends Schedule<
any,
infer Input,
any,
any
>
? Input
: never
Extracts the input type from a Schedule.
Input<function (type parameter) Schedules in <const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>>(schedules: Schedules): Schedule<Duration.Duration, UnionToIntersection<Input<Schedules[number]>>, Error<Schedules[number]>, Env<Schedules[number]>>Schedules[number]>
>,
type Error<S> = S extends Schedule<
any,
any,
infer Error,
any
>
? Error
: never
Extracts the error type from a Schedule.
Error<function (type parameter) Schedules in <const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>>(schedules: Schedules): Schedule<Duration.Duration, UnionToIntersection<Input<Schedules[number]>>, Error<Schedules[number]>, Env<Schedules[number]>>Schedules[number]>,
type Env<S> = S extends Schedule<
any,
any,
any,
infer Env
>
? Env
: never
Extracts the service requirements from a Schedule.
Env<function (type parameter) Schedules in <const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>>(schedules: Schedules): Schedule<Duration.Duration, UnionToIntersection<Input<Schedules[number]>>, Error<Schedules[number]>, Env<Schedules[number]>>Schedules[number]>
> =>
const fromStep: <
Input,
Output,
EnvX,
Error,
ErrorX,
Env
>(
step: Effect<
(
now: number,
input: Input
) => Pull.Pull<
[Output, Duration.Duration],
ErrorX,
Output,
EnvX
>,
Error,
Env
>
) => Schedule<
Output,
Input,
Error | Pull.ExcludeDone<ErrorX>,
Env | EnvX
>
Creates a Schedule from a step function that returns a Pull.
Example (Creating a custom schedule from a step function)
import { Cause, Duration, Effect, Schedule } from "effect"
const schedule = Schedule.fromStep(Effect.sync(() => {
let count = 0
return (_now: number, _input: string) => {
if (count >= 3) {
return Cause.done(count)
}
return Effect.succeed([count++, Duration.millis(100)] as [number, Duration.Duration])
}
}))
fromStep(import effecteffect.const map: {
<A, B>(f: (a: A) => B): <E, R>(
self: Effect.Effect<A, E, R>
) => Effect.Effect<B, E, R>
<A, E, R, B>(
self: Effect.Effect<A, E, R>,
f: (a: A) => B
): Effect.Effect<B, E, R>
}
map(
import effecteffect.const all: <
Arg extends
| Iterable<Effect.Effect<any, any, any>>
| Record<
string,
Effect.Effect<any, any, any>
>,
O extends {
readonly concurrency?: Concurrency | undefined
readonly discard?: boolean | undefined
readonly mode?:
| "default"
| "result"
| undefined
}
>(
arg: Arg,
options?: O
) => Effect.All.Return<Arg, O>
all(schedules: const Schedules extends NonEmptyReadonlyArray<Schedule<any, any, any, any>>schedules.map(const toStep: <Output, Input, Error, Env>(
schedule: Schedule<Output, Input, Error, Env>
) => Effect<
(
now: number,
input: Input
) => Pull.Pull<
[Output, Duration.Duration],
Error,
Output,
Env
>,
never,
Env
>
Extracts the step function from a Schedule.
Example (Extracting a schedule step function)
import { Effect, Schedule } from "effect"
// Extract step function from an existing schedule
const schedule = Schedule.exponential("100 millis").pipe(Schedule.upTo({ times: 3 }))
const program = Effect.gen(function*() {
const stepFn = yield* Schedule.toStep(schedule)
// Use the step function directly for custom logic. The timestamp is
// supplied by the caller, so tests can pass a deterministic value.
const now = 0
const result = yield* stepFn(now, "input")
console.log(`Step result: ${result}`)
})
toStep)),
(steps: unknownsteps) => (now: anynow, input: UnionToIntersection<
Input<Schedules[number]>
>
input) =>
import effecteffect.const flatMap: {
<A, B, E2, R2>(
f: (a: A) => Effect.Effect<B, E2, R2>
): <E, R>(
self: Effect.Effect<A, E, R>
) => Effect.Effect<B, E | E2, R | R2>
<A, E, R, B, E2, R2>(
self: Effect.Effect<A, E, R>,
f: (a: A) => Effect.Effect<B, E2, R2>
): Effect.Effect<B, E | E2, R | R2>
}
flatMap(
import effecteffect.const forEach: {
<
B,
E,
R,
S extends Iterable<any>,
Discard extends boolean = false
>(
f: (
a: Arr.ReadonlyArray.Infer<S>,
i: number
) => Effect.Effect<B, E, R>,
options?:
| {
readonly concurrency?:
| Concurrency
| undefined
readonly discard?: Discard | undefined
}
| undefined
): (
self: S
) => Effect.Effect<
Discard extends false
? Arr.ReadonlyArray.With<S, B>
: void,
E,
R
>
<
B,
E,
R,
S extends Iterable<any>,
Discard extends boolean = false
>(
self: S,
f: (
a: Arr.ReadonlyArray.Infer<S>,
i: number
) => Effect.Effect<B, E, R>,
options?:
| {
readonly concurrency?:
| Concurrency
| undefined
readonly discard?: Discard | undefined
}
| undefined
): Effect.Effect<
Discard extends false
? Arr.ReadonlyArray.With<S, B>
: void,
E,
R
>
}
forEach(steps: unknownsteps, (step: Arr.ReadonlyArray.Infer<S>step) =>
import PullPull.matchEffect(step: Arr.ReadonlyArray.Infer<S>step(now: anynow, input: UnionToIntersection<
Input<Schedules[number]>
>
input as never), {
onSuccess: (result: any) => Effect.Effect<A>onSuccess: (result: any(parameter) result: {
0: any;
1: Duration.Duration;
length: 2;
toString: () => string;
toLocaleString: { (): string; (locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string };
pop: () => any;
push: (...items: Array<any>) => number;
concat: { (...items: Array<ConcatArray<any>>): Array<any>; (...items: Array<any>): Array<any> };
join: (separator?: string) => string;
reverse: () => Array<any>;
shift: () => any;
slice: (start?: number, end?: number) => Array<any>;
sort: (compareFn?: ((a: any, b: any) => number) | undefined) => [any, Duration.Duration];
splice: { (start: number, deleteCount?: number): Array<any>; (start: number, deleteCount: number, ...items: Array<any>): Array<any> };
unshift: (...items: Array<any>) => number;
indexOf: (searchElement: any, fromIndex?: number) => number;
lastIndexOf: (searchElement: any, fromIndex?: number) => number;
every: { (predicate: (value: any, index: number, array: Array<any>) => value is S, thisArg?: any): this is S[]; (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any): boolean };
some: (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any) => boolean;
forEach: (callbackfn: (value: any, index: number, array: Array<any>) => void, thisArg?: any) => void;
map: (callbackfn: (value: any, index: number, array: Array<any>) => U, thisArg?: any) => Array<U>;
filter: { (predicate: (value: any, index: number, array: Array<any>) => value is S, thisArg?: any): Array<S>; (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any): Array<any> };
reduce: { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any, initialValue: any): any; (c…;
reduceRight: { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any, initialValue: any): any; (c…;
find: { (predicate: (value: any, index: number, obj: Array<any>) => value is S, thisArg?: any): S | undefined; (predicate: (value: any, index: number, obj: Array<any>) => unknown, thisArg?: any): any };
findIndex: (predicate: (value: any, index: number, obj: Array<any>) => unknown, thisArg?: any) => number;
fill: (value: any, start?: number, end?: number) => [any, Duration.Duration];
copyWithin: (target: number, start: number, end?: number) => [any, Duration.Duration];
entries: () => ArrayIterator<[number, any]>;
keys: () => ArrayIterator<number>;
values: () => ArrayIterator<any>;
includes: (searchElement: any, fromIndex?: number) => boolean;
flatMap: (callback: (this: This, value: any, index: number, array: Array<any>) => U | ReadonlyArray<U>, thisArg?: This | undefined) => Array<U>;
flat: (this: A, depth?: D | undefined) => Array<FlatArray<A, D>>;
at: (index: number) => any;
findLast: { (predicate: (value: any, index: number, array: Array<any>) => value is S, thisArg?: any): S | undefined; (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any): any };
findLastIndex: (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any) => number;
toReversed: () => Array<any>;
toSorted: (compareFn?: ((a: any, b: any) => number) | undefined) => Array<any>;
toSpliced: { (start: number, deleteCount: number, ...items: Array<any>): Array<any>; (start: number, deleteCount?: number): Array<any> };
with: (index: number, value: any) => Array<any>;
}
result) => import effecteffect.const succeed: <A>(
value: A
) => Effect.Effect<A>
succeed(result: any(parameter) result: {
0: any;
1: Duration.Duration;
length: 2;
toString: () => string;
toLocaleString: { (): string; (locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string };
pop: () => any;
push: (...items: Array<any>) => number;
concat: { (...items: Array<ConcatArray<any>>): Array<any>; (...items: Array<any>): Array<any> };
join: (separator?: string) => string;
reverse: () => Array<any>;
shift: () => any;
slice: (start?: number, end?: number) => Array<any>;
sort: (compareFn?: ((a: any, b: any) => number) | undefined) => [any, Duration.Duration];
splice: { (start: number, deleteCount?: number): Array<any>; (start: number, deleteCount: number, ...items: Array<any>): Array<any> };
unshift: (...items: Array<any>) => number;
indexOf: (searchElement: any, fromIndex?: number) => number;
lastIndexOf: (searchElement: any, fromIndex?: number) => number;
every: { (predicate: (value: any, index: number, array: Array<any>) => value is S, thisArg?: any): this is S[]; (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any): boolean };
some: (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any) => boolean;
forEach: (callbackfn: (value: any, index: number, array: Array<any>) => void, thisArg?: any) => void;
map: (callbackfn: (value: any, index: number, array: Array<any>) => U, thisArg?: any) => Array<U>;
filter: { (predicate: (value: any, index: number, array: Array<any>) => value is S, thisArg?: any): Array<S>; (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any): Array<any> };
reduce: { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any, initialValue: any): any; (c…;
reduceRight: { (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any): any; (callbackfn: (previousValue: any, currentValue: any, currentIndex: number, array: Array<any>) => any, initialValue: any): any; (c…;
find: { (predicate: (value: any, index: number, obj: Array<any>) => value is S, thisArg?: any): S | undefined; (predicate: (value: any, index: number, obj: Array<any>) => unknown, thisArg?: any): any };
findIndex: (predicate: (value: any, index: number, obj: Array<any>) => unknown, thisArg?: any) => number;
fill: (value: any, start?: number, end?: number) => [any, Duration.Duration];
copyWithin: (target: number, start: number, end?: number) => [any, Duration.Duration];
entries: () => ArrayIterator<[number, any]>;
keys: () => ArrayIterator<number>;
values: () => ArrayIterator<any>;
includes: (searchElement: any, fromIndex?: number) => boolean;
flatMap: (callback: (this: This, value: any, index: number, array: Array<any>) => U | ReadonlyArray<U>, thisArg?: This | undefined) => Array<U>;
flat: (this: A, depth?: D | undefined) => Array<FlatArray<A, D>>;
at: (index: number) => any;
findLast: { (predicate: (value: any, index: number, array: Array<any>) => value is S, thisArg?: any): S | undefined; (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any): any };
findLastIndex: (predicate: (value: any, index: number, array: Array<any>) => unknown, thisArg?: any) => number;
toReversed: () => Array<any>;
toSorted: (compareFn?: ((a: any, b: any) => number) | undefined) => Array<any>;
toSpliced: { (start: number, deleteCount: number, ...items: Array<any>): Array<any>; (start: number, deleteCount?: number): Array<any> };
with: (index: number, value: any) => Array<any>;
}
result[1]),
onDone: () => Effect.Effect<A>onDone: () => import effecteffect.const undefined: Effect<
undefined,
never,
never
>
(alias) const undefined: {
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
undefined,
onFailure: <E>(
cause: Cause.Cause<E>
) => Effect.Effect<never, E>
onFailure: import effecteffect.const failCause: <E>(
cause: Cause.Cause<E>
) => Effect.Effect<never, E>
failCause
})),
(results: unknownresults) => {
const const duration: anyduration = const minDuration: (
results: ReadonlyArray<
Duration.Duration | undefined
>
) => Duration.Duration | undefined
minDuration(results: unknownresults)
if (const duration: anyduration === var undefinedundefined) {
return import CauseCause.done(import DurationDuration.zero)
}
return import effecteffect.const succeed: <A>(
value: A
) => Effect.Effect<A>
succeed([const duration: Duration.Durationconst duration: {
value: DurationValue;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
duration, const duration: Duration.Durationconst duration: {
value: DurationValue;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
duration] as [import DurationDuration.type Duration.Duration = /*unresolved*/ anyDuration, import DurationDuration.type Duration.Duration = /*unresolved*/ anyDuration])
}
)
))