async/await: Cooperative Concurrency
async/await enables writing concurrent code that looks synchronous. Unlike threads, async tasks are cooperatively scheduled and extremely lightweight - millions can run concurrently.
// Add to Cargo.toml:
// tokio = { version = "1", features = ["full"] }
use std::time::Duration;
use tokio::time::sleep;
// async fn returns a Future - no work starts until awaited
async fn fetch_user(id: u32) -> String {
sleep(Duration::from_millis(100)).await; // Simulate network
format!("user_{}", id)
}
async fn process_user(id: u32) {
let user = fetch_user(id).await;
println!("Processed: {}", user);
}
// tokio::main sets up the async runtime
#[tokio::main]
async fn main() {
// Sequential: total ~300ms
let u1 = fetch_user(1).await;
let u2 = fetch_user(2).await;
let u3 = fetch_user(3).await;
println!("{} {} {}", u1, u2, u3);
// Concurrent with join!: total ~100ms (all run in parallel)
let (u1, u2, u3) = tokio::join!(
fetch_user(1),
fetch_user(2),
fetch_user(3)
);
println!("{} {} {}", u1, u2, u3);
// spawn: run in background
let handle = tokio::spawn(async { fetch_user(99).await });
let user = handle.await.unwrap();
println!("Background: {}", user);
}Async vs Threads: When to Use Which
| Scenario | Use |
|---|---|
| I/O bound (HTTP, DB, files) | async/await + tokio |
| CPU bound (computation) | OS threads or rayon |
| Millions of concurrent tasks | async (green threads) |
| Blocking operations | tokio::task::spawn_blocking |
🎯 Practice
- Write an async function that "fetches" 5 items concurrently using tokio::join or futures::join_all
- Use tokio::spawn to run a background task while main continues
- Use tokio::time::timeout to add a timeout to an async operation
🎉 Key Takeaways
- async fn returns a Future; .await drives it to completion
- Nothing runs until you .await or spawn
- tokio::join! runs multiple futures concurrently (not sequentially)
- async is for I/O-bound work; use OS threads for CPU-bound work