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_future 與 music_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) } #}