join!

futures::join 巨集能夠在等待多個不同的 future 完成的同時,並行執行這些 future。

當執行多個非同步操作時,很容易就寫出簡單的循序 .await


# #![allow(unused_variables)]
#fn main() {
async fn get_book_and_music() -> (Book, Music) {
    let book = get_book().await;
    let music = get_music().await;
    (book, music)
}
#}

然而,因為 get_music 不會在 get_book 完成後自動嘗試開始,這將比我們需要的更慢。在部分語言中,futurue 在哪都能執行到完成,所以兩個操作可透過第一次呼叫 async fn 來開始 future,並在之後等待它們兩者。


# #![allow(unused_variables)]
#fn main() {
// WRONG -- don't do this
async fn get_book_and_music() -> (Book, Music) {
    let book_future = get_book();
    let music_future = get_music();
    (book_future.await, music_future.await)
}
#}

不過,Rust 的 future 在直接正面地 .await 之前不會做任何事。這代表這兩段程式碼小片段將不會並行執行,而是循序執行 book_futuremusic_future。想正確並行執行兩個 future,請愛用 futures::join!


# #![allow(unused_variables)]
#fn main() {
use futures::join;

async fn get_book_and_music() -> (Book, Music) {
    let book_fut = get_book();
    let music_fut = get_music();
    join!(book_fut, music_fut)
}
#}

join! 的返回值是一個帶有每個傳入的 Future 的輸出的元組(tuple)。

try_join!

若 future 回傳的是 Result,請考慮使用 try_join! 而非 join!。由於 join! 會在所有 subfuture 完成就完成自己,甚至有 subfuture 回傳 Err 時還是會繼續處理其他 future。

join! 不同的事,當任意一個 subfuture 回傳錯誤時,try_join! 就會立刻完成。


# #![allow(unused_variables)]
#fn main() {
use futures::try_join;

async fn get_book() -> Result<Book, String> { /* ... */ Ok(Book) }
async fn get_music() -> Result<Music, String> { /* ... */ Ok(Music) }

async fn get_book_and_music() -> Result<(Book, Music), String> {
    let book_fut = get_book();
    let music_fut = get_music();
    try_join!(book_fut, music_fut)
}
#}

請注意,傳入 try_join! 的 future 需要有同樣的錯誤型別。可以考慮使用 futures::future::TryFutureExt.map_err(|e| ...)err_into() 函式來統一這些錯誤型別:


# #![allow(unused_variables)]
#fn main() {
use futures::{
    future::TryFutureExt,
    try_join,
};

async fn get_book() -> Result<Book, ()> { /* ... */ Ok(Book) }
async fn get_music() -> Result<Music, String> { /* ... */ Ok(Music) }

async fn get_book_and_music() -> Result<(Book, Music), String> {
    let book_fut = get_book().map_err(|()| "Unable to get book".to_string());
    let music_fut = get_music();
    try_join!(book_fut, music_fut)
}
#}