cod*_*ons 7 concurrency erlang rakudo reactive-programming raku
以下代码尝试对一个消息做出反应Supply,然后根据某些消息的内容改变主意并对来自不同的消息作出反应Supply。它试图提供与Supply.migrate类似的行为,但有更多的控制。
my $c1 = Supplier.new;\nmy $c2 = Supplier.new;\n\nmy $s = supply {\n my $currently-listening-to = $c1.Supply;\n my $other-var = \'foo\';\n whenever $currently-listening-to {\n say "got: $_";\n if .starts-with(\'3\') {\n say "listening to something new";\n $currently-listening-to = $c2.Supply;\n $other-var = \'bar\';\n say $other-var;\n }\n }\n}\n\n$s.tap;\n\nfor ^7 {\n $c1.emit: "$_ from \\$c1";\n $c2.emit: "$_ from \\$c2";\n}\nsleep 10;\nRun Code Online (Sandbox Code Playgroud)\n如果我正确理解块的语义supply(非常值得怀疑!),则该块应该对块内声明的任何变量具有独占且可变的访问权限supply。因此,我希望从 中获取前 4 个值$c1,然后切换到$c2. 然而,事实并非如此。这是输出:
ot: 0 from $c1\ngot: 1 from $c1\ngot: 2 from $c1\ngot: 3 from $c1\nlistening to something new\nbar\ngot: 4 from $c1\ngot: 5 from $c1\ngot: 6 from $c1\nRun Code Online (Sandbox Code Playgroud)\n正如该输出所示,更改的$other-var效果正如我预期的那样,但更改的尝试$currently-listening-to失败了(默默地)。
这种行为正确吗?如果是这样,我对解释这种行为的块/其他结构的语义缺少什么supply?react我使用块以及使用 aChannel而不是 a时得到了相同的结果Supply,因此在多个并发构造中行为是一致的。
(为了避免 XY 问题,触发这个问题的用例是尝试实现 Erlang 风格的错误处理。为此,我想要一个监督supply块来监听其子级并可以杀死/重新启动任何陷入不良状态的孩子。但这意味着要听新孩子 \xe2\x80\x93 的声音,这直接导致了上述问题。)
我倾向于认为whenever是 的反应式等价物for。(它甚至支持循环移相器在点击时LAST执行某些操作,以及支持、、 和像普通循环一样!)考虑一下:Supplydonenextlastredofor
my $x = (1,2,3);
for $x<> {
.say;
$x = (4,5,6);
}
Run Code Online (Sandbox Code Playgroud)
输出是:
1
2
3
Run Code Online (Sandbox Code Playgroud)
因为在循环的设置阶段for,我们获得一个迭代器,然后完成它,而不是$x在每次迭代时再次读取。与 相同whenever:它点击Supply然后每个事件都会调用主体emit。
因此,whenever需要另一个来实现下一个的水龙头Supply,同时关闭当前的水龙头。当只Supply考虑两个 s 时,简单的写法如下:
my $c1 = Supplier.new;
my $c2 = Supplier.new;
my $s = supply {
whenever $c1 {
say "got: $_";
if .starts-with('3') {
say "listening to something new";
# Tap the next Supply...
whenever $c2 {
say "got: $_";
}
# ...and close the tap on the current one.
last;
}
}
}
$s.tap;
for ^7 {
$c1.emit: "$_ from \$c1";
$c2.emit: "$_ from \$c2";
}
Run Code Online (Sandbox Code Playgroud)
这将产生:
got: 0 from $c1
got: 1 from $c1
got: 2 from $c1
got: 3 from $c1
listening to something new
got: 3 from $c2
got: 4 from $c2
got: 5 from $c2
got: 6 from $c2
Run Code Online (Sandbox Code Playgroud)
(请注意,我删除了 ,sleep 10因为不需要它;在此示例中我们没有引入任何并发性,因此所有内容都是同步运行的。)
显然,如果有十几个Supplys 之间需要移动,那么这种方法的扩展性就不会那么好。那么如何migrate运作呢?关键的缺失部分是我们可以Tap在使用 时获取句柄whenever,因此我们能够从 that 主体的外部关闭它whenever。这正是工作原理migrate(从标准库复制,添加注释):
method migrate(Supply:D:) {
supply {
# The Tap of the Supply we are currently emitting values from
my $current;
# Tap the Supply of Supply that we'll migrate between
whenever self -> \inner {
# Make sure we produce a sensible error
X::Supply::Migrate::Needs.new.throw
unless inner ~~ Supply;
# Close the tap on whatever we are currently tapping
$current.close if $current;
# Tap the new thing and store the Tap handle
$current = do whenever inner -> \value {
emit(value);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
简而言之:您不会更改 的目标whenever,而是开始一个新的目标whenever并终止前一个目标。
| 归档时间: |
|
| 查看次数: |
128 次 |
| 最近记录: |