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) } #}