Макрос join
#![allow(unused)] fn main() { macro_rules! join { ($(biased;)? $($future:expr),*) => { ... }; } }
Доступно только с флагом функции macros.
Ожидает завершения нескольких конкурентных ветвей, возвращая результат, когда все ветви завершатся.
Описание
Макрос join! должен использоваться внутри асинхронных функций, замыканий и блоков.
Макрос join! принимает список асинхронных выражений и выполняет их конкурентно в одной и той же задаче. Каждое асинхронное выражение вычисляется в future, и future из каждого выражения мультиплексируются в текущей задаче.
При работе с асинхронными выражениями, возвращающими Result, join! будет ждать завершения всех ветвей независимо от того, завершилась ли какая-либо из них с Err. Используйте try_join! для досрочного возврата при обнаружении Err.
Примечания
Предоставленные future хранятся inline и не требуют выделения Vec.
Характеристики времени выполнения
Поскольку все асинхронные выражения выполняются в текущей задаче, выражения могут выполняться конкурентно, но не параллельно. Это означает, что все выражения выполняются в одном потоке, и если одна ветвь блокирует поток, все другие выражения не смогут продолжить выполнение. Если требуется параллелизм, порождайте каждое асинхронное выражение с помощью tokio::spawn и передавайте дескриптор соединения в join!.
Честность (Fairness)
По умолчанию сгенерированный future макроса join! чередует порядок опроса содержащихся future при каждом пробуждении.
Это поведение можно переопределить, добавив biased; в начало использования макроса. Подробности см. в примерах. Это заставит join опрашивать future в порядке их следования сверху вниз.
Это может быть полезно, если ваши future могут взаимодействовать таким образом, что известный порядок опроса имеет значение.
Но у этого режима есть важное предостережение. Вы сами отвечаете за обеспечение справедливого порядка опроса ваших future. Если, например, вы объединяете поток и future завершения, и поток имеет огромный объем сообщений, обработка которых занимает много времени за один опрос, вам следует поместить future завершения раньше в списке join!, чтобы гарантировать, что он всегда будет опрашиваться и не будет задерживаться из-за того, что future потока долго возвращает Poll::Pending.
Примеры
Базовое объединение с двумя ветвями
#![allow(unused)] fn main() { async fn do_stuff_async() { // асинхронная работа } async fn more_async_work() { // больше асинхронной работы } let (first, second) = tokio::join!( do_stuff_async(), more_async_work() ); // делаем что-то со значениями }
Использование режима biased; для контроля порядка опроса
#![allow(unused)] fn main() { async fn do_stuff_async() { // асинхронная работа } async fn more_async_work() { // больше асинхронной работы } let (first, second) = tokio::join!( biased; do_stuff_async(), more_async_work() ); // делаем что-то со значениями }
Обработка результатов с ошибками
#![allow(unused)] fn main() { async fn fetch_data() -> Result<String, reqwest::Error> { // получение данных } async fn process_file() -> Result<Vec<u8>, std::io::Error> { // обработка файла } // join! будет ждать завершения обеих ветвей, даже если одна вернет Err let (data_result, file_result) = tokio::join!( fetch_data(), process_file() ); // Обрабатываем результаты независимо match data_result { Ok(data) => println!("Данные получены: {}", data), Err(e) => println!("Ошибка получения данных: {}", e), } match file_result { Ok(file) => println!("Файл обработан, размер: {} байт", file.len()), Err(e) => println!("Ошибка обработки файла: {}", e), } }
Использование с разными типами возвращаемых значений
#![allow(unused)] fn main() { async fn get_number() -> i32 { 42 } async fn get_text() -> &'static str { "hello" } async fn get_vector() -> Vec<u8> { vec![1, 2, 3] } let (number, text, vector) = tokio::join!( get_number(), get_text(), get_vector() ); println!("Number: {}, Text: {}, Vector: {:?}", number, text, vector); }