🎓 A1Learner
🔍
0 of 45 lessons completed

Rust Tokio Time

Tokio time is a set of timers that pause one task, not a whole thread. sleep pauses a task for one span of time.

sleep pauses one task

tokio::time::sleep pauses the task, not the thread — the runtime stays free for other tasks while this one waits:

sleep(Duration::from_millis(50)).await;
println!("woke up after 50 ms");

A sleep does nothing until you await it

sleep is a future: the pause happens at the .await, not when the future is made:

let nap = sleep(Duration::from_millis(50)); // nothing happens yet
nap.await; // the pause happens here

interval ticks at a steady rhythm

Use interval for repeating work. If your code falls behind, the default behaviour is to fire the missed ticks back-to-back until it catches up:

let mut ticker = interval(Duration::from_millis(20));
for _ in 0..3 {
    ticker.tick().await;
    println!("tick");
}

timeout puts a deadline around a job

If the future is too slow, timeout returns Err — and the future is dropped:

let result = timeout(Duration::from_millis(10), sleep(Duration::from_secs(5))).await;
match result {
    Ok(()) => println!("finished in time"),
    Err(_) => println!("too slow - gave up"),
}

An Instant plus a Duration is a deadline

Instant is Tokio's clock value. Add a Duration to now, and you have the moment by which a job must finish:

let deadline = Instant::now() + Duration::from_millis(100);
println!("deadline set = {}", deadline > Instant::now());

sleep_until waits for a moment

sleep waits for a span of time. sleep_until waits until a moment. That is the waiting half of a deadline:

let moment = Instant::now() + Duration::from_millis(30);
sleep_until(moment).await;
println!("the moment arrived");

Example

// Cargo.toml: tokio = { version = "1", features = ["full"] }
use tokio::time::{sleep, timeout, Duration};

#[tokio::main]
async fn main() {
    sleep(Duration::from_millis(50)).await;
    println!("woke up after 50 ms");

    let slow = timeout(Duration::from_millis(10), sleep(Duration::from_secs(5))).await;
    match slow {
        Ok(()) => println!("finished in time"),
        Err(_) => println!("too slow - gave up after 10 ms"),
    }
}

Key points

  • Timers cost almost nothing: one runtime can hold thousands of sleeping tasks
  • Choose what a missed tick means with set_missed_tick_behavior.
  • Burst fires the missed ticks until caught up.
  • That is the default.
  • Skip drops them and waits for the next scheduled tick.
  • Delay waits a full period from the late tick.

In this example, the first sleep pauses the task for 50 milliseconds while the runtime stays free for other work. Then timeout wraps a five-second sleep in a ten-millisecond deadline. The deadline wins, timeout returns Err, and the long sleep is dropped — the program does not wait five seconds.

Note

Never use std::thread::sleep inside a task — it blocks the worker thread for everyone, as the Blocking lesson showed.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

timeout(10ms, job).await returns Err. What happened to job?