Join

join 操作は、一連の future がすべて準備できるまで待機し、それらの結果のコレクションを返します。これは、JavaScript の Promise.all や Python の asyncio.gather に似ています。

// 著作権 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

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 で安定化される予定です。

  • join のリスクは、future の 1 つが決して解決しない可能性があることです。そうなると、プログラムは停止したままになります。

  • たとえば、HTTP サービスへのすべてのリクエストとデータベースクエリをまとめて join するために、join_alljoin! を組み合わせることもできます。futures::join! を使って、future に tokio::time::sleep を追加してみてください。これはタイムアウトではありません(タイムアウトには select! が必要で、次の章で説明します)が、join! を示しています。