Rust by Example: Async Timers
Master time-based async operations. This guide covers how to use Tokio's intervals for periodic tasks and timeouts to cancel long-running operations, essential for building responsive systems.
Code
use tokio::time::{self, Duration};
#[tokio::main]
async fn main() {
// Create an interval that ticks every 500ms
let mut interval = time::interval(Duration::from_millis(500));
println!("Starting timer...");
// Tick 5 times
for i in 1..=5 {
// The first tick completes immediately
interval.tick().await;
println!("Tick {} at {:?}", i, std::time::Instant::now());
}
println!("Timer done.");
// Timeout example
let result = time::timeout(Duration::from_millis(200), async {
println!("Sleeping for 1 second...");
time::sleep(Duration::from_secs(1)).await;
"Success"
}).await;
if let Err(_) = result {
println!("The operation timed out!");
}
}Explanation
Time-based operations are crucial in async programming. Tokio provides a robust time module. Two common tools are Intervals (for periodic tasks) and Timeouts (for cancelling slow tasks).
time::interval creates a stream of ticks. You call tick().await to wait for the next scheduled time. Tokio's interval tries to account for drift, meaning if one tick is processed slowly, the next one will fire sooner to catch up, maintaining a steady average rate.
time::timeout wraps a Future. If the inner Future completes before the duration, timeout returns Ok(value). If the duration elapses first, it returns Err(Elapsed) and cancels the inner Future (by dropping it). This is a powerful feature of Rust's async model.
Code Breakdown
time::interval(...). Creates the timer state. Note that the first tick usually fires immediately unless you configure it otherwise (e.g., with interval_at).interval.tick().await. Suspends the task until the next interval period has elapsed. It is safe to call this in a loop.time::timeout(...). This is a combinator that runs the async block. If the block takes longer than 200ms, the timeout future resolves to an Error, and the sleep future inside is dropped (cancelled).if let Err(_) = result. We use pattern matching to check if the timeout occurred. In a real app, you might retry the operation or return an error to the user.
