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) 실행되는 방식을 사용하기도 합니다. 이 방식에서는 처음부터 각 async fn을 호출하여 future들을 시작하고, 둘 모두를 기다림으로써, 두 작업이 동시에 실행될 수 있습니다.

// WRONG -- 따라하지 마시오
async fn get_book_and_music() -> (Book, Music) {
    let book_future = get_book();
    let music_future = get_music();
    (book_future.await, music_future.await)
}

하지만, 실제 러스트의 future는 .await될 때까지 아무것도 하지 않습니다. 따라서, 위의 두 코드 스니펫들은 둘 다 book_futuremusic_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들에는 join!말고 try_join!을 사용하는 게 좋습니다. join!은 모든 하위 future들이 완성되었을 때에만 완성되므로, 하위 future 들 중 하나가 Err을 반환하였더라도 나머지 future들을 계속 처리할 것입니다.

join!과 다르게, try_join!은 하위 future 중 하나가 에러를 반환하면 즉시 완성될 것입니다.

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