The following is what I see as benefits of using reactive event streams in a single-threaded synchronous application.
1. More declarative, less side-effects and less mutable state.
Event streams are capable of encapsulating logic and state, potentially leaving your code without side-effects and mutable variables.
Consider an application that counts button clicks and displays the number of clicks as a label.
Plain Java solution:
private int counter = 0; // mutable field!!!
Button incBtn = new Button("Increment");
Label label = new Label("0");
incBtn.addEventHandler(ACTION, a -> {
label.setText(Integer.toString(++counter)); // side-effect!!!
});
ReactFX solution:
Button incBtn = new Button("Increment");
Label label = new Label("0");
EventStreams.eventsOf(incBtn, ACTION)
.accumulate(0, (n, a) -> n + 1)
.map(Object::toString)
.feedTo(label.textProperty());
No mutable variable is used and the side-effectful assignment to label.textProperty()
is hidden behind an abstraction.
In his master thesis, Eugen Kiss has proposed integration of ReactFX with Scala. Using his integration, the solution could look like this:
val incBtn = new Button("Increment")
val label = new Label("0")
label.text |= EventStreams.eventsOf(incBtn, ACTION)
.accumulate(0, (n, a) => n + 1)
.map(n => n.toString)
It is equivalent to the previous, with the additional benefit of eliminating inversion of control.
2. Means to eliminate glitches and redundant computations. (ReactFX only)
Glitches are temporary inconsistencies in observable state. ReactFX has means to suspend event propagation until all updates to an object have been processed, avoiding both glitches and redundant updates. In particular, have a look at suspendable event streams, Indicator, InhiBeans and my blog post about InhiBeans. These techniques rely on the fact that event propagation is synchronous, therefore do not translate to rxJava.
3. Clear connection between event producer and event consumer.
Event bus is a global object that anyone can publish to and subscribe to. The coupling between event producer and event consumer is indirect and therefore less clear.
With reactive event streams, the coupling between producer and consumer is much more explicit. Compare:
Event bus:
class A {
public void f() {
eventBus.post(evt);
}
}
// during initialization
eventBus.register(consumer);
A a = new A();
The relationship between a
and consumer
is not clear from looking at just the initialization code.
Event streams:
class A {
public EventStream<MyEvent> events() { /* ... */ }
}
// during initialization
A a = new A();
a.events().subscribe(consumer);
The relationship between a
and consumer
is very explicit.
4. Events published by an object are manifested in its API.
Using the example from the previous section, in the event bus sample, A
's API does not tell you what events are published by instances of A
. On the other hand, in the event streams sample, A
's API states that instances of A
publish events of type MyEvent
.