Skip to main content
Types and functions for tracking time and executing code after a set period. All time types must be used from within a Tokio runtime context.

Overview

Tokio’s time module provides several primitives for time-based operations:

Sleep

Wait for a duration or until a specific instant

Interval

Execute code repeatedly at fixed intervals

Timeout

Bound the execution time of futures

Sleep

sleep

sleep
fn(duration: Duration) -> Sleep
Waits until duration has elapsed.Parameters:
  • duration: How long to sleep
Returns: A Sleep future that completes after the durationExample:
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() {
    println!("Waiting...");
    sleep(Duration::from_secs(2)).await;
    println!("2 seconds have elapsed");
}
No work is performed while awaiting on the sleep future. Sleep operates at millisecond granularity and should not be used for high-resolution timing.

sleep_until

sleep_until
fn(deadline: Instant) -> Sleep
Waits until deadline is reached.Parameters:
  • deadline: The instant to wait until
Example:
use tokio::time::{sleep_until, Instant, Duration};

#[tokio::main]
async fn main() {
    let deadline = Instant::now() + Duration::from_secs(5);
    sleep_until(deadline).await;
    println!("5 seconds have elapsed");
}

Sleep Type

The Sleep future returned by sleep and sleep_until can be pinned and reset.
reset
fn(self: Pin<&mut Self>, deadline: Instant)
Resets the Sleep instance to a new deadline without creating a new future.Example:
use tokio::time::{sleep, Duration, Instant};

#[tokio::main]
async fn main() {
    let sleep = sleep(Duration::from_secs(10));
    tokio::pin!(sleep);
    
    // Reset to 1 second
    sleep.as_mut().reset(Instant::now() + Duration::from_secs(1));
    sleep.await;
}

Interval

interval

interval
fn(period: Duration) -> Interval
Creates a new Interval that yields every period.Parameters:
  • period: Time between ticks
Returns: An Interval streamExample:
use tokio::time::{interval, Duration};

#[tokio::main]
async fn main() {
    let mut interval = interval(Duration::from_secs(1));
    
    for _ in 0..5 {
        interval.tick().await;
        println!("Tick!");
    }
}
interval measures time since the last tick, not absolute time. If processing takes time, the next tick may happen sooner to maintain the overall rate.

interval_at

interval_at
fn(start: Instant, period: Duration) -> Interval
Creates an interval that starts at a specific instant.Parameters:
  • start: When to start the first tick
  • period: Time between subsequent ticks
Example:
use tokio::time::{interval_at, Duration, Instant};

#[tokio::main]
async fn main() {
    let start = Instant::now() + Duration::from_secs(5);
    let mut interval = interval_at(start, Duration::from_secs(1));
    
    interval.tick().await; // Waits 5 seconds for first tick
    println!("First tick");
    
    interval.tick().await; // Waits 1 second
    println!("Second tick");
}

Interval Methods

tick
async fn(&mut self) -> Instant
Waits for the next tick of the interval.Returns: The instant of the tickExample:
let mut interval = interval(Duration::from_millis(100));

loop {
    interval.tick().await;
    process_periodic_task();
}
reset
fn(&mut self)
Resets the interval immediately, causing the next tick to happen after period from now.

MissedTickBehavior

MissedTickBehavior
enum
Controls behavior when ticks are missed (e.g., if processing takes longer than the period).Variants:
  • Burst: Fires all missed ticks immediately
  • Delay: Delays to maintain spacing (default)
  • Skip: Skips missed ticks
Example:
use tokio::time::{interval, MissedTickBehavior, Duration};

let mut interval = interval(Duration::from_millis(100));
interval.set_missed_tick_behavior(MissedTickBehavior::Skip);

Timeout

timeout

timeout
fn<T>(duration: Duration, future: T) -> Timeout<T>
Requires a future to complete before a duration has elapsed.Parameters:
  • duration: Maximum time to wait
  • future: The future to execute
Returns: Result<T::Output, Elapsed> - Ok with result or Err if timed outExample:
use tokio::time::{timeout, Duration};

async fn long_operation() -> String {
    tokio::time::sleep(Duration::from_secs(10)).await;
    "done".to_string()
}

#[tokio::main]
async fn main() {
    match timeout(Duration::from_secs(1), long_operation()).await {
        Ok(result) => println!("Completed: {}", result),
        Err(_) => println!("Operation timed out"),
    }
}
If the timeout elapses, the future is dropped and cancelled. Ensure proper cleanup in Drop implementations if needed.

timeout_at

timeout_at
fn<T>(deadline: Instant, future: T) -> Timeout<T>
Requires a future to complete before a deadline.Parameters:
  • deadline: The instant by which the future must complete
  • future: The future to execute
Example:
use tokio::time::{timeout_at, Instant, Duration};

async fn operation() -> u32 {
    // ... some async work
    42
}

#[tokio::main]
async fn main() {
    let deadline = Instant::now() + Duration::from_secs(5);
    
    match timeout_at(deadline, operation()).await {
        Ok(result) => println!("Result: {}", result),
        Err(_) => println!("Deadline exceeded"),
    }
}

Instant

Instant
struct
A measurement of a monotonically nondecreasing clock. Opaque and useful only with Duration.Example:
use tokio::time::{Instant, Duration};

let now = Instant::now();
// ... do some work ...
let elapsed = now.elapsed();
println!("Took {} ms", elapsed.as_millis());

Instant Methods

now
fn() -> Instant
Returns the current instant.
elapsed
fn(&self) -> Duration
Returns the time elapsed since this instant.
checked_add
fn(&self, duration: Duration) -> Option<Instant>
Adds a duration to this instant, returning None on overflow.
checked_sub
fn(&self, duration: Duration) -> Option<Instant>
Subtracts a duration from this instant, returning None on underflow.

Duration

Re-exported from std::time::Duration for convenience.
use tokio::time::Duration;

let duration = Duration::from_secs(5);
let duration = Duration::from_millis(500);
let duration = Duration::from_micros(500_000);

Practical Examples

Periodic Task Execution

use tokio::time::{interval, Duration};

async fn periodic_cleanup() {
    let mut interval = interval(Duration::from_secs(60));
    
    loop {
        interval.tick().await;
        
        // Perform cleanup
        cleanup_temp_files().await;
        check_health().await;
    }
}

Timeout with Retry

use tokio::time::{timeout, Duration, sleep};

async fn fetch_with_retry(url: &str) -> Result<String, Error> {
    for attempt in 1..=3 {
        match timeout(Duration::from_secs(5), fetch(url)).await {
            Ok(Ok(data)) => return Ok(data),
            Ok(Err(e)) => eprintln!("Attempt {} failed: {}", attempt, e),
            Err(_) => eprintln!("Attempt {} timed out", attempt),
        }
        
        if attempt < 3 {
            sleep(Duration::from_secs(2)).await;
        }
    }
    
    Err(Error::MaxRetriesExceeded)
}

Debouncing

use tokio::time::{sleep, Duration, Instant};
use tokio::sync::mpsc;

async fn debounce_events(mut rx: mpsc::Receiver<Event>) {
    let mut last_event = None;
    let debounce_duration = Duration::from_millis(300);
    
    loop {
        tokio::select! {
            Some(event) = rx.recv() => {
                last_event = Some((event, Instant::now()));
            }
            _ = sleep(debounce_duration), if last_event.is_some() => {
                if let Some((event, time)) = last_event.take() {
                    if time.elapsed() >= debounce_duration {
                        process_event(event).await;
                    }
                }
            }
        }
    }
}

Testing with Time

Tokio provides tokio::time::pause(), tokio::time::advance(), and tokio::time::resume() functions for testing time-dependent code. These require the test-util feature.
#[cfg(test)]
mod tests {
    use tokio::time::{self, Duration};
    
    #[tokio::test]
    async fn test_with_time_control() {
        time::pause();
        
        let start = time::Instant::now();
        
        tokio::spawn(async {
            time::sleep(Duration::from_secs(60)).await;
            println!("Done!");
        });
        
        time::advance(Duration::from_secs(60)).await;
        
        assert!(start.elapsed() < Duration::from_secs(1));
    }
}

Best Practices

1

Enable time driver

Ensure the runtime has the time driver enabled with enable_time() or enable_all().
2

Use appropriate granularity

Time operations work at millisecond granularity. Don’t rely on sub-millisecond precision.
3

Handle missed ticks

Configure MissedTickBehavior appropriately for your interval use case.
4

Avoid busy waiting

Always use sleep or interval rather than polling in loops.
For rate limiting, combine Semaphore with interval to control both concurrency and rate.

Build docs developers (and LLMs) love