(one: Duration.Input): Schedule<Duration.Duration>Schedule that always recurs, increasing delays by summing the preceding two delays (similar to the Fibonacci sequence). Returns the current duration between recurrences.
Example (Retrying with Fibonacci backoff)
import { Console, Data, Effect, Schedule } from "effect"
class RetryAttemptError extends Data.TaggedError("RetryAttemptError")<{ readonly message: string }> {}
// Basic Fibonacci schedule starting with 100ms
const fibSchedule = Schedule.fibonacci("100 millis")
// Delays: 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, 1300ms, ...
// Retry with Fibonacci backoff for gradual increase
const retryWithFib = Effect.gen(function*() {
let attempt = 0
const result = yield* Effect.retry(
Effect.gen(function*() {
attempt++
yield* Console.log(`Attempt ${attempt}`)
if (attempt < 5) {
return yield* Effect.fail(new RetryAttemptError({ message: `Attempt ${attempt} failed` }))
}
return `Success on attempt ${attempt}`
}),
Schedule.max([
Schedule.fibonacci("50 millis"),
Schedule.recurs(6) // Maximum 6 retries
]).pipe(
Schedule.tap(({ output: delay }) => Console.log(`Next retry in ${delay}`))
)
)
yield* Console.log(`Final result: ${result}`)
})
// Heartbeat with Fibonacci intervals (starts fast, gets slower)
const adaptiveHeartbeat = Effect.gen(function*() {
yield* Console.log("Heartbeat")
return "pulse"
}).pipe(
Effect.repeat(
Schedule.fibonacci("200 millis").pipe(
Schedule.upTo({ times: 8 }) // First 8 heartbeats
)
)
)
// Fibonacci vs exponential comparison
const compareSchedules = Effect.gen(function*() {
yield* Console.log("=== Fibonacci Delays ===")
// 100ms, 100ms, 200ms, 300ms, 500ms, 800ms
yield* Console.log("=== Exponential Delays ===")
// 100ms, 200ms, 400ms, 800ms, 1600ms, 3200ms
// Fibonacci grows more slowly than exponential
})constructors
Source effect/Schedule.ts:135014 lines
export const const fibonacci: (
one: Duration.Input
) => Schedule<Duration.Duration>
Schedule that always recurs, increasing delays by summing the preceding
two delays (similar to the Fibonacci sequence). Returns the current
duration between recurrences.
Example (Retrying with Fibonacci backoff)
import { Console, Data, Effect, Schedule } from "effect"
class RetryAttemptError extends Data.TaggedError("RetryAttemptError")<{ readonly message: string }> {}
// Basic Fibonacci schedule starting with 100ms
const fibSchedule = Schedule.fibonacci("100 millis")
// Delays: 100ms, 100ms, 200ms, 300ms, 500ms, 800ms, 1300ms, ...
// Retry with Fibonacci backoff for gradual increase
const retryWithFib = Effect.gen(function*() {
let attempt = 0
const result = yield* Effect.retry(
Effect.gen(function*() {
attempt++
yield* Console.log(`Attempt ${attempt}`)
if (attempt < 5) {
return yield* Effect.fail(new RetryAttemptError({ message: `Attempt ${attempt} failed` }))
}
return `Success on attempt ${attempt}`
}),
Schedule.max([
Schedule.fibonacci("50 millis"),
Schedule.recurs(6) // Maximum 6 retries
]).pipe(
Schedule.tap(({ output: delay }) => Console.log(`Next retry in ${delay}`))
)
)
yield* Console.log(`Final result: ${result}`)
})
// Heartbeat with Fibonacci intervals (starts fast, gets slower)
const adaptiveHeartbeat = Effect.gen(function*() {
yield* Console.log("Heartbeat")
return "pulse"
}).pipe(
Effect.repeat(
Schedule.fibonacci("200 millis").pipe(
Schedule.upTo({ times: 8 }) // First 8 heartbeats
)
)
)
// Fibonacci vs exponential comparison
const compareSchedules = Effect.gen(function*() {
yield* Console.log("=== Fibonacci Delays ===")
// 100ms, 100ms, 200ms, 300ms, 500ms, 800ms
yield* Console.log("=== Exponential Delays ===")
// 100ms, 200ms, 400ms, 800ms, 1600ms, 3200ms
// Fibonacci grows more slowly than exponential
})
fibonacci = (one: Duration.Inputone: import DurationDuration.type Duration.Input = /*unresolved*/ anyInput): 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> => {
const const oneMillis: anyoneMillis = import DurationDuration.toMillis(import DurationDuration.fromInputUnsafe(one: Duration.Inputone))
return 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 sync: <A>(
thunk: LazyArg<A>
) => Effect.Effect<A>
sync(() => {
let let a: numbera = 0
let let b: anyb = const oneMillis: anyoneMillis
return import constantconstant(import effecteffect.const sync: <A>(
thunk: LazyArg<A>
) => Effect.Effect<A>
sync(() => {
const const next: anynext = let a: numbera + let b: anyb
let a: numbera = let b: anyb
let b: anyb = const next: anynext
const 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 = import DurationDuration.millis(const next: anynext)
return [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]
}))
}))
}