Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Модуль 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::Mergefuture::RaceOk
Прерывать при ошибкеfuture::TryMergefuture::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 разных типов и структур данных
  • Эффективность: Конкурентное выполнение вместо последовательного
  • Композиционность: Легкое комбинирование операций
  • Обработка ошибок: Различные стратегии для работы с ошибочными сценариями