[项目分享] Auralis:Signal + Memo + TaskScope —— 一个纯异步 Rust 反应式内核,不是又一个框架

两个 crate,零 unsafe,信号 crate 零外部依赖。不绑 DOM、不碰渲染、没有 view! 宏——你可以把它接到任何 UI 层或数据管线上。

标题句:「反应式 = 可暂停的异步任务;生命周期 = 所有权 + 结构化并发。」

它是什么

  • auralis-signalSignal, Memo, memo! 宏, SignalMap, batch 更新, 变更检测 future。零外部依赖。

  • auralis-taskTaskScope(结构化并发)、优先级执行器、timer::sleep、上下文依赖注入、panic hook。仅依赖 auralis-signal

use auralis_signal::{Signal, memo};
use auralis_task::{TaskScope, timer};
use std::time::Duration;

let count = Signal::new(0);
let doubled = memo!(count => count.read() * 2);       // 宏自动 clone,不用手写

let ex = auralis_task::Executor::new_instance();
let scope = TaskScope::with_executor(&ex);              // 显式执行器所有权
scope.spawn(async move {
    timer::sleep(Duration::from_secs(1)).await;
    count.set(42);
});
drop(scope); // BFS 叶到根取消,200+ 层不爆栈

Demo(均可运行)

跟 Leptos / Sycamore 的信号有什么区别

Leptos 的信号是绑死在 DOM 框架里的。Auralis 是纯内核——没有 view!、没有 DOM、没有 hydration。把它理解为「你会用来构建 Leptos 的反应式内核」。

设计上比较有意思的点

  1. 延迟回调——Signal::set() 从不同步调用订阅者。通知入队到执行器下一次 flush,彻底消除重入问题。

  2. 主动 waker 注销——SignalChangedFuture drop 时立即从 signal 的订阅列表中移除自己。防止僵尸 waker 堆积——这是异步反应式系统里最容易踩的坑之一。

  3. Memo panic 安全——compute 函数 panic 后,旧订阅完好保留。下一次成功 read() 即可恢复。

  4. 迭代式 scope 取消——BFS 收集 + 叶到根取消,零递归,200+ 层嵌套不爆栈。

  5. 显式执行器所有权——TaskScope::with_executor(&ex) 持有 Rc 强引用,spawn/cancel/resume 全部通过 scope 的执行器路由。TaskScope::new() 委托到全局执行器作为便利入口。

  6. 增量 Memo 订阅——重算时共享依赖的订阅保留,只在新增/移除的信号上触发 subscribe/unsubscribe。

  7. 信号回调 panic 隔离——每个延迟回调在 executor flush 时被 catch_unwind 包裹。

  8. 故意 !Send + !Sync——Rc> 代替 Arc>。多线程场景用执行器实例隔离。

  9. Slot-based waker 路由——TaskWaker 携带 (slot_id, generation) 对,零 unsafe 代码。

性能(500K 行,3 层管道,release)

参数变化率

每帧重算

手动缓存

Auralis Memo

1%(典型 UI)

~14 ms

0.14 ms

0.16 ms

10%

~14 ms

1.45 ms

1.78 ms

50%(最坏)

~14 ms

7.33 ms

9.09 ms

缓存命中开销 < 0.01ms。真实交互频率下(<1%),Memo 有 ~90x 加速。

链接

#![forbid(unsafe_code)],127 测试全绿,clippy 零警告,fmt 洁净。欢迎试用、拍砖、提 issue。

聊天