Day 6 · Concept 17
async / await
Async functions return Futures — values that complete later. .await drives them. Unlike Go or JS, there is no built-in runtime: you pick one (almost always tokio). Cooperative multitasking; cheap tasks; not threads.
1 · The basic shape
// Cargo.toml
// tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() {
let a = fetch("api/a").await;
let b = fetch("api/b").await;
println!("{} {}", a, b);
}
async fn fetch(url: &str) -> String {
// pretend network call
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
format!("data from {}", url)
}async fn returns a Future; .await drives it.
#[tokio::main] wraps main in a runtime so you can write
async fn main directly.
2 · Run things concurrently
#[tokio::main]
async fn main() {
let (a, b) = tokio::join!(fetch("a"), fetch("b"));
println!("{} {}", a, b);
}Sequential .await = wait then wait. tokio::join! = drive both at once. The two requests overlap in time.
3 · Spawn — a task that owns its work
use tokio::task;
#[tokio::main]
async fn main() {
let h = task::spawn(async {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
"done"
});
let val = h.await.unwrap();
println!("{}", val);
}tokio::spawn launches an async task on the runtime. The returned
JoinHandle resolves to the task's output. Cheap — thousands per second is normal.
4 · async vs threads
| std::thread | tokio::spawn |
|---|---|
| OS-level. ~1 MB stack each. | User-space. ~few KB. |
| Pre-emptive scheduling. | Cooperative — yields at .await. |
| Best for CPU-bound work. | Best for I/O-bound work. |
| Can park 100s. Beyond that, expensive. | 100,000s easily. |
5 · Common mistakes
- Forgetting
.await— the future is created but never driven. Compiler warns. - Blocking the runtime with sync I/O (
std::fs) — usetokio::fs. One thread blocked = many tasks stalled. - Using std::sync::Mutex across
.await— risks deadlock. Usetokio::sync::Mutex. - Holding heavy work in an async fn. CPU loops block the executor. Use
tokio::task::spawn_blocking.
6 · When it clicks
- You reach for async for network and I/O; threads for parallel CPU.
- Sequential
.awaits feel slow — you instinctively usejoin!orselect!. - You read "future poll" docs without flinching.
Found this useful?