join!
futures::join
宏可以在 并发执行多个future的同时 等待它们完成。
join!
当进行多个异步行动时,很容易就只是简单地、连续对它们使用.await
:
async fn get_book_and_music() -> (Book, Music) {
let book = get_book().await;
let music = get_music().await;
(book, music)
}
但是这样会比理论上要慢,因为get_book
执行完后才会开始尝试执行get_music
。在一些编程语言里,future会环境运行至完成(ambiently run to completion?译注:指有些语言会在调用异步函数时就开始执行future,而非像Rust一样到被await
时才执行),因此这两个行动会在async fn
调用时就开始运行future,之后只要await它们就好了:
// 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!
:
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
的执行结果 的元组。
try_join!
对于返回Result
的future,可以考虑用try_join!
取代join!
。由于join!
只在所有子future完成时才算完成,因此即便某个子future返回了Err
,仍然会继续处理其他future。
与join!
不同的是,try_join!
会在任一子future返回Err
时立即完成
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()
来合并错误类型:
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)
}