Join

Joinという操作では、futureの集合の準備が整うまで待機し、その後に結果をまとめて返します。これはJavaScriptにおける Promise.all やPythonにおけるasyncio.gatherに似ています。

use anyhow::Result;
use futures::future;
use reqwest;
use std::collections::HashMap;

async fn size_of_page(url: &str) -> Result<usize> {
    let resp = reqwest::get(url).await?;
    Ok(resp.text().await?.len())
}

#[tokio::main]
async fn main() {
    let urls: [&str; 4] = [
        "https://google.com",
        "https://httpbin.org/ip",
        "https://play.rust-lang.org/",
        "BAD_URL",
    ];
    let futures_iter = urls.into_iter().map(size_of_page);
    let results = future::join_all(futures_iter).await;
    let page_sizes_dict: HashMap<&str, Result<usize>> =
        urls.into_iter().zip(results.into_iter()).collect();
    println!("{:?}", page_sizes_dict);
}

この例を準備したsrc/main.rsにコピーして、そこから実行してみましょう。

  • 複数の互いに素な型のfutureに対しては、std::future::join!を利用できます。しかし、いくつのfutureがコンパイル時に存在しているのかを把握しておく必要があります。これは現在futuresクレートにありますが、近いうちにstd::futureに統合される予定です。

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

  • また、join_alljoin!を組み合わせることもできます。それは、例えばデータベースのクエリと一緒にhttpサービスへの全てのリクエストをjoinする場合です。futureにfutures::join!を用いて、tokio::time::sleepを追加してみてください。これは(次のチャプターで説明する、select!を必要とする)タイムアウトではありませんが、join!の良い実演となっています。