#stream #reactive #value #subscription #data #programming #memory-leaks

epoxy_streams

epoxy_frp库提供的基础流实现。请使用epoxy_frp。

2个版本

0.3.1 2019年5月4日
0.3.0 2019年4月21日

#1621异步

每月23次下载
用于 2 crates

MIT 协议

37KB
553

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 ReactiveValue 是推送的,而不是拉取的,在某些情况下效率较低
  • Epoxy computed! <-> Carboxyl lift!
    • Epoxy computed! 宏从函数定义中提取变量,使其更易于阅读
    • Carboxyl 的 lift! 宏明确定义了输入,使其更不易出错

如您所见,这两个框架都有其权衡。Epoxy 是为了优化前端用例而设计的,这使得它更容易与 DOM 库等事物集成。

ReactiveX

这些流与 ReactiveX 系列库中的流相比要简单得多。最显著的区别是,这个库没有“冷流”的概念,这意味着没有任何流会在订阅后立即发出值。这个库中的流也永远不会关闭,因为它们旨在模拟长期异步数据流(当然,可以通过创建一个 Option 枚举的流并在 None 上取消订阅来使流“可关闭”,但这并不是库中内置的)。最后,Rx 订阅直到明确取消订阅才存在,而 Rust Reactive 订阅仅存在于其作用域内。

状态

这个包正在积极开发中,可能还不适合生产使用。

没有运行时依赖