事件(Event)
event 声明一个广播频道:emit 一次发射,所有订阅者依次收到载荷。事件可以挂在模块顶层,也可以挂在类型上成为实例事件。
声明与发射
event Ticked(n: int);
event Ping();载荷是位置参数表,emit 频道(实参); 发射事件。订阅用 ~>,退订用 !~>(未订阅也安全):
event Ticked(n: int);
func OnTick(n: int)
{
print("收到", n);
}
func OnTick2(n: int)
{
print("二号", n);
}
Ticked ~> OnTick;
Ticked ~> OnTick2;
emit Ticked(5);
Ticked !~> OnTick2;
emit Ticked(6);收到 5
二号 5
收到 6退订后只剩一个订阅者。<~ 是反向书写(OnPing <~ Ping; 等价 Ping ~> OnPing),适合「谁消费谁订阅」的阅读顺序。
派发顺序与去重
派发按订阅顺序(先订阅先派发),同步完成:emit 的调用栈里逐个执行 handler,与声明顺序无关:
event Ticked(n: int);
Ticked ~> (n: int) => print("h2", n);
Ticked ~> (n: int) => print("h1", n);
Ticked ~> (n: int) => print("h3", n);
emit Ticked(1);h2 1
h1 1
h3 1同一 handler 对同一事件重复订阅会去重:只占一个槽位、派发一次,一次 !~> 即全部移除。
重入
handler 执行中再 emit(同一事件或别的)是合法的:内层立即递归派发,栈式嵌套,内层完整跑完才回到外层。派发中途退订自己不影响本轮,自己从下一轮起不再收到:
event A(n: int);
A ~> (n: int) =>
{
print("enter", n);
if n < 2
{
emit A(n + 1);
}
print("exit", n);
};
emit A(1);enter 1
enter 2
exit 2
exit 1handler 的形状
handler 可以是具名函数或 lambda。返回值不受约束,声明 -> int 也合法,返回值被忽略。
订阅 handler 须是普通函数:无 fail、无 uses、非 async。
实例事件
事件挂在类上就是实例事件,派发表按实例隔离:
class Counter
{
public event Changed(from: int, to: int);
public var value: int = 0;
public func Set(v: int)
{
val old = value;
value = v;
emit Changed(old, v);
}
}
func OnCh(from: int, to: int)
{
print("chg", from, "->", to);
}
val g = new Counter();
g.Changed ~> OnCh;
g.Set(1);
g.Changed !~> OnCh;
g.Set(2);chg 0 -> 1实例事件的退订 g.Changed !~> h 与顶层同款。实例丢弃最后引用后订阅是否自动失效由宿主 GC 决定,语言不做承诺,句柄的 IsCancelled 不因此变为 True。
事件属性
事件属性(event prop)更进一步:写入即广播,载荷固定是 (旧值, 新值),且带变化判断,第二次 = 5 没有产生广播:
class Gauge
{
public event prop Level: int;
}
val g = new Gauge();
g.Level ~> (oldV: int, newV: int) => { print($"液位 {oldV} -> {newV}"); };
g.Level = 5;
g.Level = 5;
g.Level = 8;液位 0 -> 5
液位 5 -> 8变化判断发生在 before 管线改写之后。同值写入跳过的是整条写链,存储、after 段与自动 emit 全部跳过,只有 finally 恒跑,且与有没有订阅者无关(管线段语法见事件管线)。
泛型事件
event Changed<T>(v: T); 仅顶层可声明,实例化即通道。类型实参三路可推:显式实例化(Changed<int> ~> onInt)、handler 形参推断(Changed ~> (v: int) => ...)、emit 实参推断(emit Changed(43);)。
不同实例化是不同频道:Changed<int> 的订阅者收不到 emit Changed("s"),两个频道互不相通,也不报错。
