Tasks & JoinHandles
Tokio tasks are lightweight units of async work scheduled on the runtime. JoinHandle lets you await results, abort execution, and track task completion.
Busca en todas las páginas de la documentación
Tokio tasks are lightweight units of async work scheduled on the runtime. JoinHandle lets you await results, abort execution, and track task completion.
Quick-reference recipe card - copy-paste ready.
#[tokio::main]
async fn main() {
let handle = tokio::spawn(async { 42 });
let answer = handle.await.unwrap();
println!("{answer}");
}When to reach for this: Background work, per-connection handlers, parallel I/O, and structured task groups.
use tokio::task::JoinSet;
async fn fetch_id(id: u32) -> u32 {
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
id * 10
}
#[tokio::main]
async fn main() {
let mut set = JoinSet::new();
for id in 1..=5 {
set.spawn(fetch_id(id));
}
while let Some(res) = set.join_next().await {
println!("{}", res.unwrap());
}
}What this demonstrates:
JoinSet manages dynamic spawned tasks.join_next yields results as tasks complete.await returns Result<T, JoinError>.Vec.| API | Use |
|---|---|
tokio::spawn | Send tasks on runtime |
spawn_blocking | Blocking closure on blocking pool |
spawn_local | !Send on LocalSet |
task::Builder | Name tasks for tracing |
// Named task for tokio-console / tracing
let handle = tokio::task::Builder::new()
.name("worker")
.spawn(async { /* ... */ })
.unwrap();JoinError is is_panic() or is_cancelled().JoinSet, or attach tracing error handler.spawn_local + LocalSet or remove Rc/mutex guards across await.spawn_blocking for sync sections.await returns Err(JoinError). Fix: Match error; consider catch_unwind for isolation.| Alternative | Use When | Don't Use When |
|---|---|---|
tokio::join! | Few futures on same task | Long-lived background workers |
FuturesUnordered | Not spawned; same task | Need true parallel OS scheduling |
| OS threads | CPU isolation, blocking APIs | Thousands of lightweight I/O tasks |
| Structured scopes (nightly patterns) | Strict parent-child cancel | Simple one-shot spawn |
Tasks are cooperatively scheduled on few threads - cheaper, but must not block.
Task still runs but errors are lost - anti-pattern in production.
abort cancels spawned task; dropping handle does not cancel the task.
JoinSet drives completion efficiently; Vec + manual join works for small fixed counts.
Tokio may yield periodically - long CPU loops need yield_now().await.
Runs !Send tasks on one thread - tests and single-threaded embed scenarios.
Handlers are tasks; spawn subtasks for background work that outlives response carefully.
#[tracing::instrument] on async fns + named tasks improves observability.
Distinguish panic vs cancel - cancel is expected on shutdown.
No hard limit - bounded by memory and FDs; use semaphores for backpressure.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 19 jul 2026