join! is an asynchronous macro that polls multiple futures at the same time and finishes when every future completes. Unlike consecutive .await calls, it lets independent async work make progress together. section 19.3.4
Three delayed values
Call wait_and_give_u8(1), wait_and_give_u8(2), and wait_and_give_u8(3) inside join!. Each function waits for a random duration before printing. The printed order can vary, but the resulting tuple remains (1, 2, 3), preserving argument order. section 19.3.4
use rand::*;
use tokio::join;
use std::time::Duration;
async fn wait_and_give_u8(num: u8) -> u8 {
let mut rng = rand::thread_rng();
let wait_time = rng.gen_range(1..100);
tokio::time::sleep(Duration::from_millis(wait_time)).await;
println!("Got a number! {num}");
num
}
#[tokio::main]
async fn main() {
let nums = join!(
wait_and_give_u8(1),
wait_and_give_u8(2),
wait_and_give_u8(3)
);
println!("{nums:?}");
}
A runnable Tokio example shows the join! invocation and its tuple result. section 19.3.4
Choose related async controls by the completion behavior you need:
- Use join! when all futures must finish; use select! when the first completed future should determine the result. section 19.3.4
- Use try_join! when joined futures return Results and an error should produce a failed join. section 19.3.4

