13个版本
0.6.7 | 2022年5月17日 |
---|---|
0.6.6 | 2021年10月1日 |
0.6.4 | 2021年8月4日 |
0.6.3 | 2021年7月29日 |
#13 in #data-pipeline
1,479 每月下载量
在 enet-client 中使用
52KB
1K SLoC
Eventuals提供了某些值的最新快照。它们就像随着时间的推移而更新的Futures,不断解析到最终一致的状态。您可以链式调用eventuals,检查它们的当前值,并订阅更新。eventuals范式是异步编程中的许多艰难教训的结晶。
考虑以下使用由JavaScript启发的虚构set_interval的代码
set_interval(|| async { let update = fetch().await; set_ui_data(update); }, 2000)
以上是用于在一段时间内从外部数据更新UI的典型模式。这段代码有几个问题。首先,fetch操作可能会重叠。这种重叠可能导致在显示新数据后显示过时的数据!此外,没有明确的方法可以取消 - 无论是从读取还是写入的角度来看。
Eventuals解决了这些问题和其他常见问题!
let cancel_on_drop = timer(2000).map(|_| fetch).pipe(set_ui_update);
在这里,set_ui_update
保证只按时间顺序前进。不同的管道阶段可以同时执行,如果可用新值,则可能会跳过一些更新。尽管如此,最终状态始终与数据的最终写入保持一致。
订阅
订阅是从读取者的角度对eventual的最新更新的视图。只有当值自上次观察以来已更改时,订阅才会观察更新,并且可能会跳过上次观察和当前观察之间的更新。总的来说,这确保了任何读取者都只执行必要的最少工作量,以与写入者的状态最终一致。
您可以通过调用eventual.subscribe()
和subscription.next().await
来获取订阅,在任何您准备好处理更新的时刻。如果自上次处理的更新以来已进行了更新,则Future会立即准备好,否则它将在更新可用时解决。
快照
获取未订阅的eventual的当前值有两种方式。这些是 eventual.value_immediate()
和 eventual.value
,具体取决于你是否想要等待第一次更新。
单元测试
eventual的一个有用应用是使组件可测试而无需模拟。你不必制作一个模拟服务器(这需要特质),而是可以向你的组件提供一个eventual并输入数据。这不是依赖注入,而是数据注入。
常见问题解答
What is the difference between an eventual and a Stream?
Stream是一组异步提供的有序且唯一的值的集合。然而,eventual是对单个值随时间变化的一种异步视图。
The API seems sparse. I see map and join, but where are filter, reduce, and select?
想要将所有功能技术应用到eventual上是自然的,但这是一种反模式。eventual的目标是使任何数据管道任何视图的最终值与最新的写入确定性一致。产生结果的过程可能不是确定性的,但如果每个构建块都是一致的,最终值是一致的。并非每个功能块都通过这个测试。为了演示,我们可以比较map和一个假想的filter组合器在相同序列的写入上的行为。
考虑以下两个函数:eventual.map(|n| async move { n * 2 })
和 eventual.filter(|n| async move { n % 2 == 0 })
,这两个函数都随着时间的推移消耗序列的写入 [0, 1, 2, 3, 4, 5]
。在这种情况下,map
总会最终解析为值 10
。它也可能在途中产生一些中间值的子集(如 [0, 4, 8, 10]
或 [2, 10]
)。尽管如此,它总会随着时间的推移向前发展,并始终解析为与最终写入一致的确定值。然而,filter
最终产生的值将取决于观察到的中间值。如果 filter
观察到写入子集 [0, 2, 5]
,它将解析为 2
,但如果它观察到写入子集 [1, 4, 5]
,它将解析为 4
。这个错误正是终态试图避免的类型。
依赖关系
~3.5–9.5MB
~90K SLoC