20 个版本

0.11.4 2023年3月21日
0.11.2 2021年1月5日
0.3.3 2020年12月30日

异步 中排名第 294

Download history 32/week @ 2024-03-11 20/week @ 2024-03-18 55/week @ 2024-03-25 76/week @ 2024-04-01 78/week @ 2024-04-08 34/week @ 2024-04-15 10/week @ 2024-04-22 1/week @ 2024-04-29 1/week @ 2024-05-13 32/week @ 2024-05-20 23/week @ 2024-05-27 20/week @ 2024-06-03 33/week @ 2024-06-10 44/week @ 2024-06-17 17/week @ 2024-06-24

每月下载量 115

MIT/Apache

40KB
282

async-fuse

github crates.io docs.rs build status

为“融合”异步计算提供辅助工具。

一旦操作完成,融合操作将具有明确的行為。对于 Fuse 来说,这意味着已经完成的操作将通过返回 Poll::Pending 来永远阻塞。

这类似于在 futures-rs 中提供的 Fuse 类型,但它提供了更多的实用功能,允许它与不实现 FusedFutureFusedStream 的类型进行交互,就像 1.0 版本的 Tokio 类型所做的那样。

我们还使用 Fuse 来表示可选值,就像 Option 一样。但 Fuse 提供了实现和函数,允许我们在值被固定时安全地执行操作。这正是驱动一个 Stream(请参阅 next)或轮询一个可能或可能未设置 Future 所必需的。


特性

  • stream - 如果 Fuse 包含一个流,则使其实现 Stream 特性。

简化 tokio::select!

Fuse 的主要用途之一是简化我们使用 tokio::select! 的方式。在本节中,我们将探讨如何改进一个可选分支,其中被轮询的 future 可能或可能未设置。

let mut maybe_future = Some(async { 42u32 });
tokio::pin!(maybe_future);

tokio::select! {
    value = async { maybe_future.as_mut().as_pin_mut().unwrap().await }, if maybe_future.is_some() => {
        maybe_future.set(None);
        assert_eq!(value, 42);
    }
    /* other branches */
}

assert!(maybe_future.is_none());

上面的 async 块是必要的,因为无论分支预条件(见 这里)如何,都会“贪婪”地轮询未来。这会导致在未设置未来时,unwrap 会引发恐慌。完成之后,我们还需要显式地将 pin 设置为 None。否则,我们可能会在稍后轮询它,这可能会导致恐慌。

使用 Fuse,我们可以重写分支并移除 async 块。它还在完成之后为我们取消设置未来。

use async_fuse::Fuse;

let mut maybe_future = Fuse::new(async { 42u32 });
tokio::pin!(maybe_future);

tokio::select! {
    value = &mut maybe_future, if !maybe_future.is_empty() => {
        assert_eq!(value, 42);
    }
    /* other branches */
}

assert!(maybe_future.is_empty());

最后,如果我们不需要 else 分支 进行评估,我们可以完全跳过 分支预条件。这使我们能够进一步减少代码。

use async_fuse::Fuse;

let mut maybe_future = Fuse::new(async { 42u32 });
tokio::pin!(maybe_future);

tokio::select! {
    value = &mut maybe_future => {
        assert_eq!(value, 42);
    }
    /* other branches */
}

assert!(maybe_future.is_empty());

栈上融合

对于第一个示例,我们将使用 tokio::pin! 在栈上融合值。我们还将更新融合,在它完成时使用可配置延迟的另一个睡眠。模仿 Interval 的行为。

这可以作为 stack_ticker 示例使用

cargo run --example stack_ticker
use async_fuse::Fuse;
use std::time::Duration;
use tokio::time;

let mut duration = Duration::from_millis(500);

let sleep = Fuse::new(time::sleep(duration));
tokio::pin!(sleep);

let update_duration = Fuse::new(time::sleep(Duration::from_secs(2)));
tokio::pin!(update_duration);

for _ in 0..10usize {
    tokio::select! {
        _ = &mut sleep => {
            println!("Tick");
            sleep.set(Fuse::new(time::sleep(duration)));
        }
        _ = &mut update_duration => {
            println!("Tick faster!");
            duration = Duration::from_millis(250);
        }
    }
}

堆上融合

对于某些类型,可能在堆上融合值更容易。为了使其更简单,我们提供了 Fuse::pin 构造函数,它提供了一个在堆上固定的融合值。

因此,它与上面的例子看起来非常相似。

这可以作为 heap_ticker 示例使用

cargo run --example heap_ticker
use async_fuse::Fuse;
use std::time::Duration;
use tokio::time;

let mut duration = Duration::from_millis(500);

let mut sleep = Fuse::pin(time::sleep(duration));
let mut update_duration = Fuse::pin(time::sleep(Duration::from_secs(2)));

for _ in 0..10usize {
    tokio::select! {
        _ = &mut sleep => {
            println!("Tick");
            sleep.set(Box::pin(time::sleep(duration)));
        }
        _ = &mut update_duration => {
            println!("Tick faster!");
            duration = Duration::from_millis(250);
        }
    }
}

融合特质对象

以下展示了我们如何融合特质对象。特质对象很有用,因为它们允许融合值在不同的实现之间切换。代价是我们执行动态分派,这会有一些成本。

请注意,由于 CoerceUnsized 尚未稳定,我们不能为了方便而使用 Fuse::pin,并必须通过 Fuse::new 传递一个固定箱。

这可以作为 trait_object_ticker 示例使用

cargo run --example trait_object_ticker
use async_fuse::Fuse;
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use tokio::time;

let mut duration = Duration::from_millis(500);

let mut sleep: Fuse<Pin<Box<dyn Future<Output = ()>>>> =
    Fuse::new(Box::pin(time::sleep(duration)));

let mut update_duration: Fuse<Pin<Box<dyn Future<Output = ()>>>> =
    Fuse::new(Box::pin(time::sleep(Duration::from_secs(2))));

for _ in 0..10usize {
    tokio::select! {
        _ = &mut sleep => {
            println!("Tick");
            sleep.set(Box::pin(time::sleep(duration)));
        }
        _ = &mut update_duration => {
            println!("Tick faster!");
            duration = Duration::from_millis(250);
        }
    }
}

依赖