下面的代码尝试对一个Supply
做出React,然后根据一些消息的内容改变主意,对来自另一个Supply
的消息做出React。它试图提供与Supply.migrate类似的行为,但具有更多的控制。
my $c1 = Supplier.new;
my $c2 = Supplier.new;
my $s = supply {
my $currently-listening-to = $c1.Supply;
my $other-var = 'foo';
whenever $currently-listening-to {
say "got: $_";
if .starts-with('3') {
say "listening to something new";
$currently-listening-to = $c2.Supply;
$other-var = 'bar';
say $other-var;
}
}
}
$s.tap;
for ^7 {
$c1.emit: "$_ from \$c1";
$c2.emit: "$_ from \$c2";
}
sleep 10;
如果我正确理解了supply
块的语义(非常值得怀疑!),这个块应该对supply
块中声明的任何变量都有独占和可变的访问权限。因此,我期望它从$c1
中获取前4个值,然后切换到$c2
。然而,它没有。下面是输出:
ot: 0 from $c1
got: 1 from $c1
got: 2 from $c1
got: 3 from $c1
listening to something new
bar
got: 4 from $c1
got: 5 from $c1
got: 6 from $c1
如输出所示,更改$other-var
的工作正如我所期望的那样,但是更改$currently-listening-to
的尝试失败了(没有提示)。
这种行为是否正确?如果是,我在解释这种行为的supply
块/其他结构的语义方面遗漏了什么?我在使用react
块和使用Channel
而不是Supply
时得到了相同的结果,因此这种行为在多个并发结构中是一致的。
(In为了避免X-Y问题,触发这个问题的用例是尝试实现Erlang风格的错误处理。为此,我希望有一个监督supply
块,它监听其子块,并可以杀死/重新启动任何进入坏状态的子块。但这意味着监听新的子块-这直接导致了上述问题。)
1条答案
按热度按时间balp4ylt1#
I tend to consider
whenever
as the reactive equivalent offor
. (It even supports theLAST
loop phaser for doing something when the tappedSupply
isdone
, as well as supportingnext
,last
, andredo
like an ordinaryfor
loop!) Consider this:The output is:
Because at the setup stage of a
for
loop, we obtain an iterator, and then work through that, not reading$x
again on each iteration. It's the same withwhenever
: it taps theSupply
and then the body is invoked peremit
event.Thus another
whenever
is needed to achieve a tap of the nextSupply
, while simultaneously closing the tap on the current one. When there are just twoSupply
s under consideration, the easy way to write it is like this:Which will produce:
(Note that I removed the
sleep 10
because there's no need for it; we aren't introducing any concurrency in this example, so everything runs synchronously.)Clearly, if there were a dozen
Supply
s to move between then this approach won't scale so well. So how doesmigrate
work? The key missing piece is that we can obtain theTap
handle when working withwhenever
, and thus we are able to close it from outside of the body of thatwhenever
. This is exactly howmigrate
works (copied from the standard library, with comments added):In short: you don't change the target of the
whenever
, but rather start a newwhenever
and terminate the previous one.