Модуль future
Базовая асинхронная функциональность.
Пожалуйста, ознакомьтесь с ключевыми словами async и await и книгой по асинхронному программированию для получения дополнительной информации об асинхронном программировании в Rust.
Примеры
#![allow(unused)] fn main() { use futures_concurrency::prelude::*; use futures_lite::future::block_on; use std::future; block_on(async { // Ожидание нескольких future одинакового типа. let a = future::ready(1); let b = future::ready(2); let c = future::ready(3); assert_eq!([a, b, c].join().await, [1, 2, 3]); // Ожидание нескольких future разных типов. let a = future::ready(1u8); let b = future::ready("hello"); let c = future::ready(3u16); assert_eq!((a, b, c).join().await, (1, "hello", 3)); // Это также работает с векторами future, предоставляя альтернативу // `join_all` из futures-rs. let a = future::ready(1); let b = future::ready(2); let c = future::ready(3); assert_eq!(vec![a, b, c].join().await, vec![1, 2, 3]); }) }
Конкурентность
Часто операции зависят от результата нескольких future. Вместо последовательного ожидания каждого future может быть более эффективным ожидать их конкурентно. Rust предоставляет встроенные механизмы в библиотеке, чтобы сделать это простым и удобным.
Безошибочная конкурентность
При работе с future, которые не возвращают типы Result, мы предоставляем две встроенные операции конкурентности:
future::Merge: ожидать завершения всех future в набореfuture::Race: ожидать завершения первого future в наборе
Поскольку future можно рассматривать как асинхронную последовательность из одного элемента, см. раздел конкурентности асинхронных итераторов для дополнительных операций асинхронной конкурентности.
Ошибочная конкурентность
При работе с future, которые возвращают типы Result, значение существующих операций меняется, и становятся доступными дополнительные операции конкурентности, учитывающие Result:
| Ожидание всех результатов | Ожидание первого результата | |
|---|---|---|
| Продолжать при ошибке | future::Merge | future::RaceOk |
| Прерывать при ошибке | future::TryMerge | future::Race |
future::TryMerge: ожидать успешного завершения всех future в наборе или вернуться при первой ошибке.future::RaceOk: ожидать завершения первого успешного future в наборе или вернутьErr, если ни один future не завершился успешно.
Дополнительные примеры
Использование TryMerge с ошибочными future
#![allow(unused)] fn main() { use futures_concurrency::prelude::*; let success = async { Ok::<i32, &str>(42) }; let error = async { Err::<i32, &str>("something went wrong") }; let another_success = async { Ok::<i32, &str>(100) }; let result = [success, error, another_success].try_join().await; assert!(result.is_err()); // Прерывается на первой ошибке }
Использование RaceOk для получения первого успешного результата
#![allow(unused)] fn main() { use futures_concurrency::prelude::*; use std::time::Duration; use tokio::time::sleep; let fast_error = async { sleep(Duration::from_millis(10)).await; Err::<i32, &str>("fast error") }; let slow_success = async { sleep(Duration::from_millis(50)).await; Ok::<i32, &str>(42) }; let medium_success = async { sleep(Duration::from_millis(30)).await; Ok::<i32, &str>(100) }; let result = [fast_error, slow_success, medium_success].race_ok().await; assert_eq!(result, Ok(100)); // Первый успешный результат }
Использование FutureGroup для динамического управления future
#![allow(unused)] fn main() { use futures_concurrency::prelude::*; use futures_concurrency::future::FutureGroup; let mut group = FutureGroup::new(); // Добавляем future в группу group.push(async { 1 }); group.push(async { 2 }); group.push(async { 3 }); // Ожидаем завершения всех future в группе let results = group.join().await; assert_eq!(results, vec![1, 2, 3]); }
Практический пример с сетевыми запросами
use futures_concurrency::prelude::*; use reqwest::Client; async fn fetch_data(client: &Client, url: &str) -> Result<String, reqwest::Error> { let response = client.get(url).send().await?; response.text().await } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = Client::new(); let urls = vec![ "https://httpbin.org/ip", "https://httpbin.org/user-agent", "https://httpbin.org/headers" ]; // Параллельное выполнение всех запросов let futures: Vec<_> = urls.iter() .map(|url| fetch_data(&client, url)) .collect(); let results = futures.try_join().await?; for (i, result) in results.iter().enumerate() { println!("Ответ {}: {} символов", i, result.len()); } Ok(()) }
Модули
| Имя | Описание |
|---|---|
future_group | Расширяемая группа future, которые действуют как единое целое. |
Структуры
| Имя | Описание |
|---|---|
FutureGroup | Расширяемая группа future, которые действуют как единое целое. |
WaitUntil | Приостанавливает future до указанного крайнего срока. |
Трейты
| Имя | Описание |
|---|---|
FutureExt | Трейт-расширение для трейта Future. |
Join | Ожидание завершения всех future. |
Race | Ожидание завершения первого future. |
RaceOk | Ожидание завершения первого успешного future. |
TryJoin | Ожидание успешного завершения всех future или досрочное прерывание при ошибке. |
Ключевые преимущества
- Типобезопасность: Статическая проверка типов на этапе компиляции
- Гибкость: Работа с future разных типов и структур данных
- Эффективность: Конкурентное выполнение вместо последовательного
- Композиционность: Легкое комбинирование операций
- Обработка ошибок: Различные стратегии для работы с ошибочными сценариями