在函数式响应式编程中,"故障"的定义是什么?
我知道在一些FRP框架中可能发生"故障",而在其他框架中则不会发生。例如,RX不是无故障的,而ReactFX是无故障的[1]。
是否有人能给出一个非常简单的例子来说明在使用RX时如何以及何时发生故障,并在同一个例子上显示相应的ReactFX解决方案如何以及为什么没有故障?
定义
我最喜欢的定义:
故障是可观察状态下的暂时不一致。
定义来自Scala。处方:
在FRP上下文中,故障是数据流图中的暂时不一致。由于更新不会立即发生,而是需要时间来计算,因此在更新过程中,FRP系统中的值可能会暂时不同步。此外,根据FRP系统的性质,有可能在传播中有节点被更新不止一次。
考虑整数变量a
, b
。定义sum
和prod
,使sum := a + b
, prod := a * b
.
让我们将这个例子重写为JavaFX:
IntegerProperty a = new SimpleIntegerProperty();
IntegerProperty b = new SimpleIntegerProperty();
NumberBinding sum = a.add(b);
NumberBinding prod = a.multiply(b);
现在让我们写一点一致性检查:
InvalidationListener consistencyCheck = obs -> {
assert sum.intValue() == a.get() + b.get();
assert prod.intValue() == a.get() * b.get();
};
sum.addListener(consistencyCheck);
prod.addListener(consistencyCheck);
a.set(1);
b.set(2);
这段代码失败了,最后一行出现了一个断言错误,因为:
-
b
更新(到2)-
sum
更新(到3)- ' consistencyCheck '被触发,' a == 1 ', ' b == 2 ',但' prod == 0 ',因为' prod '尚未更新
-
这是一个小故障—prod
暂时与a
和b
不一致
使用ReactFX消除故障
首先要注意的是,ReactFX 不是"无故障"的,但是它为您提供了消除故障的工具。除非你有意识地去使用它们,否则ReactFX不会比RX(例如rxJava)更无故障。在ReactFX中消除故障的技术依赖于事件传播是同步的这一事实。另一方面,RX中的事件传播总是异步的,因此这些技术不能在RX系统中实现。
在上面的例子中,我们希望延迟侦听器通知,直到sum
和prod
都被更新。下面是如何使用ReactFX实现这一点:
import org.reactfx.Guardian;
import org.reactfx.inhibeans.binding.Binding;
IntegerProperty a = new SimpleIntegerProperty();
IntegerProperty b = new SimpleIntegerProperty();
Binding<Number> sum = Binding.wrap(a.add(b)); // Binding imported from ReactFX
Binding<Number> prod = Binding.wrap(a.multiply(b)); // Binding imported from ReactFX
InvalidationListener consistencyCheck = obs -> {
assert sum.getValue().intValue() == a.get() + b.get();
assert prod.getValue().intValue() == a.get() * b.get();
};
sum.addListener(consistencyCheck);
prod.addListener(consistencyCheck);
// defer sum and prod listeners until the end of the block
Guardian.combine(sum, prod).guardWhile(() -> {
a.set(1);
b.set(2);
});
简短回答:glitch =不一致/非法/无意义的状态。
这里有一个相关链接:https://social.msdn.microsoft.com/Forums/en-US/bc2c4b71-c97b-428e-ad71-324055a3cd03/another-discussion-on-glitches-and-rx?forum=rx
另外,请参阅钠的作者谈话的第29分钟的另一个答案:http://youtu.be/gaG3tIb3Lbk。
和一个相关的SOF答案:如何避免Rx中的故障
根据Tomas的回答,这是我对故障的理解。
有一个数据流图,有3个节点:a, B, C
A - B>
C ->
在这个简单的例子中,如果我更改了a,导致更改了B,但C还没有更新,就会发生故障。这是一个故障。
C与b不一致
假设B=2*A, C=2*A
如果B不等于C,那么这是一个故障
下面是c# RX中致命"故障"情况的一个非常简短的理论示例
var t = Observable
.Interval(TimeSpan.FromSeconds(1))
.Publish()
.RefCount();
var s = t.CombineLatest(t, (t1,t2) => 1/(1-(t1-t2));
由于t1
和t2
都代表了热点观测t
的最新值,因此可以假设t1-t2
总是0
。所以s应该是1
。
但是当下标s时,我们确实得到1
作为第一个观察值,但随后我们得到一个除零异常。在RxJS中,我们会得到NaN
。
原因很简单:当a
或b
产生一个值时,a.CombineLatest(b, f)
将做出反应,将这个新值与另一个可观测值的最后观测值结合起来。这是设计上的,但是根据我的经验,使用RX的人有时会认为这些是故障,特别是当来自其他具有不同"最新"概念的FRP库时。
这当然是一个人为的例子,只是为了说明对CombineLatest
的误解。
也许CombineLatest
应该被称为WhenAny
在ReactiveUI
库,这将澄清操作语义?