21个不稳定版本 (4个重大更改)
0.6.3 | 2024年7月27日 |
---|---|
0.6.0 | 2024年5月4日 |
0.5.2 | 2024年3月29日 |
0.2.2 | 2023年10月26日 |
0.2.0 | 2023年7月26日 |
#7 in #事件系统
1,875 每月下载量
14KB
179 行
starbase_events
为 starbase
应用程序框架提供的异步事件发射器。这个crate与其他事件系统的工作方式相当不同,因为订阅者可以修改事件数据。正因为如此,我们不能使用消息通道,必须采取额外预防措施来满足 Send
+ Sync
的要求。
创建事件
事件必须派生自 Event
或实现 Event
特性。
use starbase_events::Event;
use app::Project;
#[derive(Debug, Event)]
pub struct ProjectCreatedEvent(pub Project);
事件数据
事件可以包含可选的数据,该数据传递给订阅者,并可以由订阅者修改。默认情况下,值是单元类型 (()
),但可以通过 #[event]
为派生事件自定义,或在手动实现时使用 type Data
。
use starbase_events::Event;
use std::path::PathBuf;
#[derive(Event)]
#[event(dataset = PathBuf)]
pub struct CacheCheckEvent(pub PathBuf);
// OR
pub struct CacheCheckEvent(pub PathBuf);
impl Event for CacheCheckEvent {
type Data = PathBuf;
}
创建发射器
发射器负责管理订阅者,并将事件分发给每个订阅者,同时考虑执行流程和一次性订阅者。
每个事件都需要自己的发射器实例。
use starbase_events::Emitter;
let project_created = Emitter::<ProjectCreatedEvent>::new();
let cache_check: Emitter<CacheCheckEvent> = Emitter::new();
使用订阅者
订阅者是异步函数,被注册到发射器中,并在发射器发射事件时执行。它们接收事件对象作为 Arc<T>
,以及事件的数据作为 Arc<RwLock<T::Data>>
,允许事件不可变地引用,并可以可变或不可变地访问其数据。
use starbase_events::{Event, EventResult, EventState};
async fn update_root(
event: Arc<ProjectCreatedEvent>,
data: Arc<RwLock<<ProjectCreatedEvent as Event>::Data>>
) -> EventResult {
let mut data = data.write().await;
data.root = new_path;
Ok(EventState::Continue)
}
emitter.on(subscriber).await; // Runs multiple times
emitter.once(subscriber).await; // Only runs once
此外,我们提供了一个简化函数实现的 #[subscriber]
函数属性。例如,上面的订阅者可以被重写为
#[subscriber]
async fn update_root(mut data: ProjectCreatedEvent) {
data.root = new_path;
}
当使用 #[subscriber]
时,以下好处适用
- 返回类型是可选的。
- 如果
EventState::Continue
,则返回值是可选的。 - 使用
mut event
或&mut Event
将获取数据的写入锁,否则获取读锁。 - 省略事件参数将不会获取任何锁。
- 参数的名称用于 数据,而事件仅仅是
event
。
控制事件流
订阅者可以通过返回 EventState
来控制事件执行流程,它支持以下变体
Continue
- 继续到下一个订阅者(默认)。Stop
- 在此订阅者之后停止,丢弃后续订阅者。
#[subscriber]
async fn continue_flow(mut event: CacheCheckEvent) {
Ok(EventState::Continue)
}
#[subscriber]
async fn stop_flow(mut event: CacheCheckEvent) {
Ok(EventState::Stop)
}
发射和处理结果
当事件被发射时,订阅者在同一线程中按顺序执行,以便每个订阅者可以在必要时修改事件。因此,事件不支持内部值的引用/生命周期,而必须拥有所有内容。
事件可以通过 emit()
方法发射,该方法需要一个拥有的事件(以及拥有的内部数据)。
let data = emitter.emit(ProjectCreatedEvent(owned_project)).await?;
发射在所有修改完成后返回事件数据。
依赖关系
~3.5–5MB
~80K SLoC