(duration: Duration.Input): Schedule<number>Returns a schedule that recurs continuously, each repetition spaced the specified duration from the last run.
When to use
Use when each delay should start after the previous action completes.
Example (Repeating with fixed spacing)
import { Console, Effect, Schedule } from "effect"
// Basic spaced schedule - runs every 2 seconds
const everyTwoSeconds = Schedule.spaced("2 seconds")
// Heartbeat that runs indefinitely with fixed spacing
const heartbeat = Effect.gen(function*() {
yield* Console.log("Heartbeat")
}).pipe(
Effect.repeat(everyTwoSeconds)
)
// Limited repeat - run only 5 times with 1-second spacing
const limitedTask = Effect.gen(function*() {
yield* Console.log("Executing scheduled task...")
yield* Effect.sleep("500 millis") // simulate work
return "Task completed"
}).pipe(
Effect.repeat(
Schedule.spaced("1 second").pipe(Schedule.upTo({ times: 5 }))
)
)
// Simple spaced schedule with limited repetitions
const limitedSpaced = Schedule.max([
Schedule.spaced("100 millis"),
Schedule.recurs(5) // at most 5 times
])
const program = Effect.gen(function*() {
yield* Console.log("Starting spaced execution...")
yield* Effect.repeat(
Effect.succeed("work item"),
limitedSpaced
)
yield* Console.log("Completed executions")
})export const const spaced: (
duration: Duration.Input
) => Schedule<number>
Returns a schedule that recurs continuously, each repetition spaced the
specified duration from the last run.
When to use
Use when each delay should start after the previous action completes.
Example (Repeating with fixed spacing)
import { Console, Effect, Schedule } from "effect"
// Basic spaced schedule - runs every 2 seconds
const everyTwoSeconds = Schedule.spaced("2 seconds")
// Heartbeat that runs indefinitely with fixed spacing
const heartbeat = Effect.gen(function*() {
yield* Console.log("Heartbeat")
}).pipe(
Effect.repeat(everyTwoSeconds)
)
// Limited repeat - run only 5 times with 1-second spacing
const limitedTask = Effect.gen(function*() {
yield* Console.log("Executing scheduled task...")
yield* Effect.sleep("500 millis") // simulate work
return "Task completed"
}).pipe(
Effect.repeat(
Schedule.spaced("1 second").pipe(Schedule.upTo({ times: 5 }))
)
)
// Simple spaced schedule with limited repetitions
const limitedSpaced = Schedule.max([
Schedule.spaced("100 millis"),
Schedule.recurs(5) // at most 5 times
])
const program = Effect.gen(function*() {
yield* Console.log("Starting spaced execution...")
yield* Effect.repeat(
Effect.succeed("work item"),
limitedSpaced
)
yield* Console.log("Completed executions")
})
spaced = (duration: Duration.Inputduration: import DurationDuration.type Input =
| number
| bigint
| Duration.Duration
| readonly [seconds: number, nanos: number]
| `${number} nano`
| `${number} nanos`
| `${number} micro`
| `${number} micros`
| `${number} milli`
| `${number} millis`
| `${number} second`
| `${number} seconds`
| `${number} minute`
| `${number} minutes`
| `${number} hour`
| `${number} hours`
| `${number} day`
| `${number} days`
| `${number} week`
| `${number} weeks`
| "Infinity"
| "-Infinity"
| Duration.DurationObject
Valid input types that can be converted to a Duration.
When to use
Use when an API should accept any value that Effect can convert into a
Duration, including existing durations, millisecond numbers, nanosecond
bigints, high-resolution tuples, duration strings, infinity strings, or
duration objects.
Details
String inputs accept values like "10 seconds", "500 millis",
"Infinity", and "-Infinity". Finite fractional values that are
normalized to nanoseconds are rounded to the nearest nanosecond, with ties
away from zero.
Input): 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 decoded: Duration.Durationconst decoded: {
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;
}
decoded = import DurationDuration.const fromInputUnsafe: (
input: Input
) => Duration
Decodes a Duration.Input into a Duration.
When to use
Use when the input has already been validated or comes from a trusted source
and throwing is acceptable for invalid duration syntax.
Gotchas
If the input is not a valid Duration.Input, it throws an error.
Example (Decoding duration inputs)
import { Duration } from "effect"
const duration1 = Duration.fromInputUnsafe(1000) // 1000 milliseconds
const duration2 = Duration.fromInputUnsafe("5 seconds")
const duration3 = Duration.fromInputUnsafe("Infinity")
const duration4 = Duration.fromInputUnsafe([2, 500_000_000]) // 2 seconds and 500ms
fromInputUnsafe(duration: Duration.Inputduration)
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 succeed: <A>(
value: A
) => Effect.Effect<A>
succeed((meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta) => import effecteffect.const succeed: <A>(
value: A
) => Effect.Effect<A>
succeed([meta: InputMetadata<unknown>(parameter) meta: {
input: Input;
attempt: number;
start: number;
now: number;
elapsed: number;
elapsedSincePrevious: number;
}
meta.InputMetadata<Input>.attempt: numberattempt - 1, const decoded: Duration.Durationconst decoded: {
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;
}
decoded])))
}