(interval: Duration.Input): Schedule<number>Returns a Schedule that recurs on the specified fixed interval and
outputs the number of repetitions of the schedule so far.
When to use
Use when recurrences should stay aligned to a regular cadence.
Gotchas
If the action run between recurrences takes longer than the interval, the next recurrence happens immediately, but missed intervals are not replayed.
|-----interval-----|-----interval-----|-----interval-----|
|---------action--------||action|-----|action|-----------|Example (Repeating on fixed intervals)
import { Console, Effect, Schedule } from "effect"
// Fixed interval schedule - recurs on a one-second cadence
const everySecond = Schedule.fixed("1 second")
// Health check that runs at fixed intervals
const healthCheck = Effect.gen(function*() {
yield* Console.log("Health check")
yield* Effect.sleep("200 millis") // simulate health check work
return "healthy"
}).pipe(
Effect.repeat(Schedule.fixed("2 seconds").pipe(Schedule.upTo({ times: 5 })))
)
// Difference between fixed and spaced:
// - fixed: maintains constant rate regardless of action duration
// - spaced: waits for the duration AFTER each action completes
const longRunningTask = Effect.gen(function*() {
yield* Console.log("Task started")
yield* Effect.sleep("1.5 seconds") // Longer than interval
yield* Console.log("Task completed")
return "done"
})
// Fixed schedule: if task takes 1.5s but interval is 1s,
// next execution happens immediately (no pile-up)
const fixedSchedule = longRunningTask.pipe(
Effect.repeat(Schedule.fixed("1 second").pipe(Schedule.upTo({ times: 3 })))
)
// Comparing with spaced (waits 1s AFTER each task)
const spacedSchedule = longRunningTask.pipe(
Effect.repeat(Schedule.spaced("1 second").pipe(Schedule.upTo({ times: 3 })))
)
const program = Effect.gen(function*() {
yield* Console.log("=== Fixed Schedule Demo ===")
yield* fixedSchedule
yield* Console.log("=== Spaced Schedule Demo ===")
yield* spacedSchedule
})export const const fixed: (
interval: Duration.Input
) => Schedule<number>
Returns a Schedule that recurs on the specified fixed interval and
outputs the number of repetitions of the schedule so far.
When to use
Use when recurrences should stay aligned to a regular cadence.
Gotchas
If the action run between recurrences takes longer than the interval, the
next recurrence happens immediately, but missed intervals are not replayed.
|-----interval-----|-----interval-----|-----interval-----|
|---------action--------||action|-----|action|-----------|
Example (Repeating on fixed intervals)
import { Console, Effect, Schedule } from "effect"
// Fixed interval schedule - recurs on a one-second cadence
const everySecond = Schedule.fixed("1 second")
// Health check that runs at fixed intervals
const healthCheck = Effect.gen(function*() {
yield* Console.log("Health check")
yield* Effect.sleep("200 millis") // simulate health check work
return "healthy"
}).pipe(
Effect.repeat(Schedule.fixed("2 seconds").pipe(Schedule.upTo({ times: 5 })))
)
// Difference between fixed and spaced:
// - fixed: maintains constant rate regardless of action duration
// - spaced: waits for the duration AFTER each action completes
const longRunningTask = Effect.gen(function*() {
yield* Console.log("Task started")
yield* Effect.sleep("1.5 seconds") // Longer than interval
yield* Console.log("Task completed")
return "done"
})
// Fixed schedule: if task takes 1.5s but interval is 1s,
// next execution happens immediately (no pile-up)
const fixedSchedule = longRunningTask.pipe(
Effect.repeat(Schedule.fixed("1 second").pipe(Schedule.upTo({ times: 3 })))
)
// Comparing with spaced (waits 1s AFTER each task)
const spacedSchedule = longRunningTask.pipe(
Effect.repeat(Schedule.spaced("1 second").pipe(Schedule.upTo({ times: 3 })))
)
const program = Effect.gen(function*() {
yield* Console.log("=== Fixed Schedule Demo ===")
yield* fixedSchedule
yield* Console.log("=== Spaced Schedule Demo ===")
yield* spacedSchedule
})
fixed = (interval: Duration.Inputinterval: 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<number> => {
const const window: anywindow = import DurationDuration.toMillis(import DurationDuration.fromInputUnsafe(interval: Duration.Inputinterval))
return const fromStepWithMetadata: <
Input,
Output,
EnvX,
ErrorX,
Error,
Env
>(
step: Effect<
(
options: InputMetadata<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 receives metadata about the schedule's execution.
Example (Creating a metadata-aware schedule)
import { Cause, Duration, Effect, Schedule } from "effect"
const firstThreeInputs = Schedule.fromStepWithMetadata(Effect.succeed((metadata: Schedule.InputMetadata<string>) => {
if (metadata.attempt > 3) {
return Cause.done("finished")
}
return Effect.succeed([
`attempt ${metadata.attempt}: ${metadata.input}`,
Duration.millis(250)
] as [string, Duration.Duration])
}))
fromStepWithMetadata(import effecteffect.const sync: <A>(
thunk: LazyArg<A>
) => Effect.Effect<A>
sync(() => {
let let start: numberstart = 0
let let lastRun: numberlastRun = 0
return (meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta) =>
import effecteffect.const sync: <A>(
thunk: LazyArg<A>
) => Effect.Effect<A>
sync(() => {
if (const window: anywindow === 0) {
return [meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.attempt - 1, import DurationDuration.zero] as type const = readonly [number, any]const
}
if (meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.attempt === 1) {
let start: numberstart = meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.now
let lastRun: numberlastRun = meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.now + const window: anywindow
return [0, import DurationDuration.millis(const window: anywindow)] as type const = readonly [0, any]const
}
const const runningBehind: booleanrunningBehind = meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.now > (let lastRun: numberlastRun + const window: anywindow)
const const boundary: numberboundary = const window: anywindow - ((meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.now - let start: numberstart) % const window: anywindow)
const const delay: anydelay = const runningBehind: booleanrunningBehind ? 0 : const boundary: numberboundary === 0 ? const window: anywindow : const boundary: numberboundary
let lastRun: numberlastRun = const runningBehind: booleanrunningBehind ? meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.now : meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.now + const delay: anydelay
return [meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.attempt - 1, import DurationDuration.millis(const delay: anydelay)] as type const = readonly [number, any]const
})
}))
}