2个版本
0.3.1 | 2019年5月4日 |
---|---|
0.3.0 | 2019年4月21日 |
#24 in #input-stream
在 epoxy_frp 中使用
42KB
649 行
Rust的响应式流。
Rust方式的响应式编程
该库提供了2个基本响应式编程原语。 Stream
表示一个无状态的管道,可以订阅和处理异步数据。 ReactiveValue
表示一个值可以随时间变化的数据(类似于Atomic,但由流支持,以便在值变化时通知依赖项)。这两个原语与Rx库系列中的 'Stream' 和 'BehaviorSubject' 相当松散地对应。
该库的一个独特特性是流订阅只持续到订阅对象的作用域,从而防止了响应式代码中常见的许多内存泄漏和僵尸回调问题。
let stream_host: epoxy::Sink<i32> = epoxy::Sink::new();
let stream = stream_host.get_stream();
{
let _sub = stream.subscribe(|val| println!("Emitted {}", val));
stream_host.emit(1); // 'Emitted 1' is printed
assert_eq!(stream.count_subscribers(), 1);
}
stream_host.emit(2); // Nothing is printed
assert_eq!(stream.count_subscribers(), 0);
可以使用基于Rust迭代操作集的内置函数库来操作流。目前这些操作包括
操作 | 返回流的属性 |
---|---|
map(fn) | 将输入流的所有值通过映射函数运行 |
map_rc(fn) | 与map()相同,但映射函数接收并返回一个Arc |
flat_map(fn) | 类似于map(),但迭代映射函数的结果 |
filter(fn) | 仅返回通过给定过滤函数的输入值 |
inspect(method) | 传递原始流,对每个项目调用一个方法 |
scan(fn, default) | 类似于reduce(),但返回每次迭代的值 |
count_values() | 返回流已发射的次数 |
buffer(size) | 将发射的值收集到长度为 size 的向量中 |
ReactiveValues有其自己的操作符集,尽管也可以通过 .as_stream()
获取ReactiveValue的底层流引用,并使用上述任何操作。
操作 | 返回的响应式值的属性 |
---|---|
map(fn) | 将输入流的所有值通过映射函数运行 |
sanitize(fn, default) | 如果输入未通过测试函数,则不会改变值 |
回退(fn, fallback) | 如果输入未通过测试函数,则将值更改为 fallback |
然而,这个库还附带了一个 computed!
宏,这使得处理 ReactiveValue 与处理任何其他 Rust 变量一样简单。
# #[macro_use] extern crate epoxy;
use epoxy::ReactiveValue;
let points = epoxy::ReactiveValue::new(4);
let multiplier = epoxy::ReactiveValue::new(1);
let score = computed!(points * multiplier);
assert_eq!(*score.get(), 4);
multiplier.set(2);
assert_eq!(*score.get(), 8);
与其他 FRP 库的比较
Carboxyl / Frappe
Carboxyl 和 Frappe 目前是 Rust 中最常用的两个 FRP 库(我将它们合并在一起,因为它们的结构非常相似)。这个库受到 ReactiveX 的启发,而不是 Carboxyl 和 Frappe 所使用的 FRP 论文,因此这里的某些术语有所不同。还有一些显著的 API 差异
- Epoxy 订阅 <-> Carboxyl 观察者
- Epoxy 中的订阅在超出作用域时取消订阅
- Carboxyl 观察者在观察函数返回 False-y 时取消订阅
- Epoxy ReactiveValue <-> Carboxyl 信号
- Carboxyl 信号不能被订阅(观察),这对于 UI 框架来说是一个问题
- Epoxy ReactiveValues 是推式,而不是拉式,在某些情况下效率较低
- Epoxy computed! <-> Carboxyl lift!
- Epoxy computed! 宏从函数定义中提取变量,使其更易于阅读
- Carboxyl 的 lift! 宏具有显式定义的输入,使其更不易出错
如您所见,这两个框架都有权衡之处。Epoxy 被设计为优化前端用例,使其更容易与 DOM 库等集成。
ReactiveX
这些流比 ReactiveX 家族库中的流要简单得多。最显著的区别是,这个库没有“冷”流的概念,这意味着没有任何流会在订阅时立即发出值。这个库中的流也永远不会关闭,因为它们旨在模拟长期异步数据流(当然,通过使流成为 Option 枚举的流并在 None
上取消订阅,可以使流“可关闭”,但这并不是库中内置的)。最后,与 Rx 订阅一直存在直到显式取消订阅不同,Rust Reactive 订阅只存在于它们的作用域内。
状态
这个包正在积极开发中,可能还不适合生产使用。
依赖关系
~2MB
~46K SLoC