Time-based operations including delays, timeouts, scheduling, and timing measurements. Cats Effect provides comprehensive temporal operations that integrate with the cancellation and concurrency system.
Basic time-based pausing and delay operations.
/**
* Sleep for specified duration
* @param duration - How long to sleep
* @returns IO[Unit] that completes after the duration
*/
def IO.sleep(duration: FiniteDuration): IO[Unit]
/**
* Add delay before executing IO
* @param duration - Delay duration
* @returns IO[A] that executes after delay
*/
def delayBy(duration: FiniteDuration): IO[A]
/**
* Add delay after executing IO
* @param duration - Delay duration
* @returns IO[A] that delays after completion
*/
def andWait(duration: FiniteDuration): IO[A]Apply time limits to IO operations with various fallback strategies.
/**
* Apply timeout to IO operation
* @param duration - Maximum time allowed
* @returns IO[A] that fails with TimeoutException if exceeded
*/
def timeout(duration: FiniteDuration): IO[A]
/**
* Apply timeout with fallback value
* @param duration - Maximum time allowed
* @param fallback - IO to run if timeout occurs
* @returns IO[AA] using fallback on timeout
*/
def timeoutTo[AA >: A](duration: FiniteDuration, fallback: IO[AA]): IO[AA]
/**
* Apply timeout without cancellation
* @param duration - Maximum time allowed
* @returns IO[A] that continues running but returns None on timeout
*/
def timeoutAndForget(duration: FiniteDuration): IO[Option[A]]Measure execution time and add timing information.
/**
* Measure execution time of IO
* @returns IO[(FiniteDuration, A)] with duration and result
*/
def timed: IO[(FiniteDuration, A)]
/**
* Get current monotonic time (for measuring intervals)
* @returns IO[FiniteDuration] with monotonic time
*/
def IO.monotonic: IO[FiniteDuration]
/**
* Get current real time (wall clock time)
* @returns IO[FiniteDuration] with real time since epoch
*/
def IO.realTime: IO[FiniteDuration]
/**
* Get current time in milliseconds
* @returns IO[Long] with current time millis
*/
def Clock[IO].realTime: IO[FiniteDuration]
/**
* Get monotonic time in nanoseconds
* @returns IO[Long] with monotonic nanos
*/
def Clock[IO].monotonic: IO[FiniteDuration]Control timing of repeated and scheduled operations.
/**
* Repeat IO with fixed delay between executions
* @param delay - Delay between executions
* @returns IO that repeats with delay
*/
def IO.sleep(delay: FiniteDuration).foreverM: IO[Nothing]
/**
* Run IO at fixed intervals (approximate)
* @param interval - Target interval between starts
* @param fa - IO to run repeatedly
* @returns IO[Nothing] that runs forever at intervals
*/
def fixedRate[A](interval: FiniteDuration)(fa: IO[A]): IO[Nothing]
/**
* Run IO with fixed delay between completions
* @param delay - Delay between completion and next start
* @param fa - IO to run repeatedly
* @returns IO[Nothing] that runs with fixed delays
*/
def fixedDelay[A](delay: FiniteDuration)(fa: IO[A]): IO[Nothing]Advanced temporal coordination between multiple operations.
/**
* Race IO against a timeout
* @param duration - Timeout duration
* @returns IO[Either[Unit, A]] - Left(()) for timeout, Right(a) for success
*/
def raceTimeout(duration: FiniteDuration): IO[Either[Unit, A]]
/**
* Apply maximum execution time (similar to timeout but different semantics)
* @param duration - Maximum duration
* @returns IO[A] that fails if duration exceeded
*/
def within(duration: FiniteDuration): IO[A]
/**
* Ensure minimum execution time
* @param duration - Minimum duration
* @returns IO[A] that waits if completing too quickly
*/
def guaranteeMinimum(duration: FiniteDuration): IO[A]Lower-level clock operations for custom timing logic.
/**
* Get Clock instance for IO
* @returns Clock[IO] for time operations
*/
implicit def Clock[IO]: Clock[IO]
/**
* Measure elapsed time for a computation
* @param fa - IO to measure
* @returns IO[(FiniteDuration, A)] with elapsed time
*/
def Clock[IO].timed[A](fa: IO[A]): IO[(FiniteDuration, A)]
/**
* Current time since Unix epoch
* @returns IO[FiniteDuration] with real time
*/
def Clock[IO].realTime: IO[FiniteDuration]
/**
* Monotonic time for measuring intervals
* @returns IO[FiniteDuration] with monotonic time
*/
def Clock[IO].monotonic: IO[FiniteDuration]Working with duration values and time units.
// Import for duration syntax
import scala.concurrent.duration._
// Duration construction examples:
val oneSecond: FiniteDuration = 1.second
val fiveHundredMillis: FiniteDuration = 500.millis
val twoMinutes: FiniteDuration = 2.minutes
val oneHour: FiniteDuration = 1.hour
// Duration operations
val total = 1.second + 500.millis
val half = 2.seconds / 2
val comparison = 1.second < 2.secondsUsage Examples:
import cats.effect._
import scala.concurrent.duration._
// Basic sleep and delay
val sleepProgram = for {
_ <- IO.println("Starting...")
_ <- IO.sleep(1.second)
_ <- IO.println("After 1 second")
} yield ()
// Timeout with fallback
val timeoutProgram = {
val slowOperation = IO.sleep(5.seconds).as("Slow result")
val fastFallback = IO.pure("Fast fallback")
slowOperation.timeoutTo(2.seconds, fastFallback)
}
// Measuring execution time
val timedProgram = for {
(duration, result) <- IO.delay {
Thread.sleep(1000)
"Computation result"
}.timed
_ <- IO.println(s"Took ${duration.toMillis}ms: $result")
} yield result
// Fixed rate execution (runs every 30 seconds)
def healthCheck: IO[Unit] =
IO.println(s"Health check at ${java.time.Instant.now()}")
val healthMonitor = fixedRate(30.seconds)(healthCheck)
// Racing with timeout
val racingProgram = for {
result <- IO.delay("Quick result").delayBy(100.millis)
.raceTimeout(1.second)
message = result match {
case Left(()) => "Operation timed out"
case Right(value) => s"Got result: $value"
}
_ <- IO.println(message)
} yield result
// Coordinated timing
val coordinatedProgram = for {
start <- IO.monotonic
_ <- IO.println("Starting coordinated operations")
// Multiple operations with different timing
fiber1 <- IO.sleep(500.millis).as("Task 1").start
fiber2 <- IO.sleep(1.second).as("Task 2").start
fiber3 <- IO.sleep(1.5.seconds).as("Task 3").start
// Wait for all to complete
results <- List(fiber1, fiber2, fiber3)
.traverse(_.joinWithNever)
end <- IO.monotonic
_ <- IO.println(s"All completed in ${(end - start).toMillis}ms")
_ <- IO.println(s"Results: ${results.mkString(", ")}")
} yield results
// Retry with exponential backoff
def retryWithBackoff[A](
operation: IO[A],
maxRetries: Int,
baseDelay: FiniteDuration
): IO[A] = {
def attempt(retriesLeft: Int, delay: FiniteDuration): IO[A] = {
operation.handleErrorWith { error =>
if (retriesLeft > 0) {
IO.println(s"Operation failed, retrying in ${delay.toMillis}ms...") >>
IO.sleep(delay) >>
attempt(retriesLeft - 1, delay * 2)
} else {
IO.raiseError(error)
}
}
}
attempt(maxRetries, baseDelay)
}
val retriedOperation = retryWithBackoff(
operation = IO.raiseError(new RuntimeException("Flaky operation")),
maxRetries = 3,
baseDelay = 100.millis
)