20 个版本
0.11.4 | 2023年3月21日 |
---|---|
0.11.2 | 2021年1月5日 |
0.3.3 | 2020年12月30日 |
在 异步 中排名第 294
每月下载量 115
40KB
282 行
async-fuse
为“融合”异步计算提供辅助工具。
一旦操作完成,融合操作将具有明确的行為。对于 Fuse
来说,这意味着已经完成的操作将通过返回 Poll::Pending
来永远阻塞。
这类似于在 futures-rs 中提供的 Fuse
类型,但它提供了更多的实用功能,允许它与不实现 FusedFuture
或 FusedStream
的类型进行交互,就像 1.0 版本的 Tokio 类型所做的那样。
我们还使用 Fuse
来表示可选值,就像 Option
一样。但 Fuse
提供了实现和函数,允许我们在值被固定时安全地执行操作。这正是驱动一个 Stream
(请参阅 next
)或轮询一个可能或可能未设置 Future
所必需的。
特性
简化 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);
}
}
}