Join

A join operation waits until all of a set of futures are ready, and returns a collection of their results. This is similar to Promise.all in JavaScript or asyncio.gather in Python.

  1. use anyhow::Result;
  2. use futures::future;
  3. use reqwest;
  4. use std::collections::HashMap;
  5. async fn size_of_page(url: &str) -> Result<usize> {
  6.     let resp = reqwest::get(url).await?;
  7.     Ok(resp.text().await?.len())
  8. }
  9. #[tokio::main]
  10. async fn main() {
  11.     let urls: [&str; 4] = [
  12.         "https://google.com",
  13.         "https://httpbin.org/ip",
  14.         "https://play.rust-lang.org/",
  15.         "BAD_URL",
  16.     ];
  17.     let futures_iter = urls.into_iter().map(size_of_page);
  18.     let results = future::join_all(futures_iter).await;
  19.     let page_sizes_dict: HashMap<&str, Result<usize>> =
  20.         urls.into_iter().zip(results.into_iter()).collect();
  21.     println!("{page_sizes_dict:?}");
  22. }

This slide should take about 4 minutes.

Copy this example into your prepared src/main.rs and run it from there.

  • For multiple futures of disjoint types, you can use std::future::join! but you must know how many futures you will have at compile time. This is currently in the futures crate, soon to be stabilised in std::future.

  • The risk of join is that one of the futures may never resolve, this would cause your program to stall.

  • You can also combine join_all with join! for instance to join all requests to an http service as well as a database query. Try adding a tokio::time::sleep to the future, using futures::join!. This is not a timeout (that requires select!, explained in the next chapter), but demonstrates join!.