Introduction
By default, .await is sequential — each future runs one after another. join!, try_join!, JoinSet, and join_all let you run futures concurrently on the same task.
Key Concepts
Sequential (slow):
rustlet a = fetch_a().await; // 100ms let b = fetch_b().await; // 100ms // Total: ~200ms
Concurrent with join!:
rustlet (a, b) = tokio::join!(fetch_a(), fetch_b()); // Total: ~100ms — both run concurrently
join! polls all futures on the same task (not separate threads) and returns when all complete.
Real World Context
Parallel API calls, concurrent database queries, fetching a user's profile + posts + followers simultaneously — any time you need multiple independent I/O results.
Deep Dive
try_join! for Result-returning futures — short-circuits on first error:
rustlet (user, posts) = tokio::try_join!( fetch_user(id), fetch_posts(id), )?; // Returns early if either fails
JoinSet for dynamic task counts:
rustlet mut set = JoinSet::new(); for url in urls { set.spawn(async move { fetch(url).await }); } while let Some(result) = set.join_next().await { handle(result?); }
futures::future::join_all for homogeneous future collections:
rustlet futures: Vec<_> = urls.iter().map(|u| fetch(u)).collect(); let results = futures::future::join_all(futures).await;
Common Pitfalls
- Using
join!when you need early error return — usetry_join!instead - Confusing
join!(concurrent on one task) withspawn(separate tasks) join_allwith thousands of futures — considerbuffer_unorderedinstead
Best Practices
- Use
join!/try_join!for a fixed, small number of futures - Use
JoinSetfor dynamic or large sets of spawned tasks - Use
buffer_unorderedon streams for bounded concurrency
Summary
join! runs futures concurrently and waits for all. try_join! adds early error return. JoinSet manages dynamic task groups. join_all handles homogeneous collections.
Code Examples
// Parallel API fetching with try_join!
async fn get_dashboard(user_id: u64) -> Result<Dashboard, Error> {
let (profile, posts, notifications) = tokio::try_join!(
fetch_profile(user_id),
fetch_posts(user_id),
fetch_notifications(user_id),
)?;
Ok(Dashboard { profile, posts, notifications })
}