Hyperlinkv0.8.0-beta.28

Schedule

Schedule.fixedconsteffect/Schedule.ts:1436
(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
})
constructorsspaced
export const fixed = (interval: Duration.Input): Schedule<number> => {
  const window = Duration.toMillis(Duration.fromInputUnsafe(interval))
  return fromStepWithMetadata(effect.sync(() => {
    let start = 0
    let lastRun = 0
    return (meta) =>
      effect.sync(() => {
        if (window === 0) {
          return [meta.attempt - 1, Duration.zero] as const
        }
        if (meta.attempt === 1) {
          start = meta.now
          lastRun = meta.now + window
          return [0, Duration.millis(window)] as const
        }
        const runningBehind = meta.now > (lastRun + window)
        const boundary = window - ((meta.now - start) % window)
        const delay = runningBehind ? 0 : boundary === 0 ? window : boundary
        lastRun = runningBehind ? meta.now : meta.now + delay
        return [meta.attempt - 1, Duration.millis(delay)] as const
      })
  }))
}