join!

futures::join宏等待并发执行的多个不同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_book完成后再get_music。在其它编程语言 中,future是运行至完成的,所以两个操作可以通过先调起async fn来启动future,然后再分别 await他们来并发操作: However, this will be slower than necessary, since it won't start trying to get_music until after get_book has completed. In some other languages, futures are ambiently run to completion, so two operations can be run concurrently by first calling the each async fn to start the futures, and then awaiting them both:


# #![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的输出的元组。

try_join!

对于那些返回Result的future,考虑使用try_join!而非join。因为join只会在所有子 future都完成后才会完成,它甚至会在子future返回Err之后继续处理。

join!不同,try_join!会在其中的字future返回错误后立即完成。


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