I am trying to integrate ReactiveX into my GUI using RxPY. This is a general ReactiveX question.
Say I have a visualization that depends on multiple Observable streams using combine_latest(stream1, stream2, plot_function)
. This works great when one Observable changes, like when the user modifies a value; the visualization updates using the new values. However, sometimes both Observables are updated simultaneously, like when the user loads data for both streams from a single file. Technically, one Observable will be updated before the other (whichever comes first in the import function). As a result, the plot will be updated twice, but for all intents and purposes it need only be updated once.
Some of the visualizations I have are expensive to compute, so I want to make sure that if both streams are updated simultaneously, then the combined stream only emits one value. I can think of a few options:
Use
debounce()
with a small timeout (like 50ms) on the combined stream. This approach seems dirty to me.Don't use
combine_latest
directly. Wrap the two streams in a new object that also has some sort ofupdating
flag. If I set theupdating
flag to True, then don't emit anything until I set theupdating
flag to False. This approach feels to stateful, and it ruins the composability of the streams.Tell all visualizations not to update until all the streams are updated. Again, this breaks encapsulation because the visualization shouldn't care what is happening upstream. It should just receive the new values from the combined stream and make a pretty picture.
Make the visualizations fine-grained enough that one stream updating first only introduces a small performance penalty. This is impossible for some visualizations, like a visualization that computes a mesh based on points and a mesh size. If the points or the mesh size changes, then the whole mesh needs to be recomputed.
Is there some facility in Rx to handle "simultaneously" updating multiple streams? I feel like what I'm asking for is magic.
For anyone who has made GUI programs using Rx: is there some better architecture I should be using for models besides sending new values through streams?
If this question is unclear, please tell me in a comment and I will try to make a more concrete example.
Example
Here is a sample Python RxPY program:
import rx
stream1 = rx.subjects.BehaviorSubject(1)
stream2 = rx.subjects.BehaviorSubject(2)
rx.Observable\
.combine_latest(stream1, stream2, lambda x, y: (x, y))\
.subscribe(print)
stream1.on_next(3)
stream2.on_next(4)
This prints:
(1, 2)
(3, 2)
(3, 4)
How could I update the values of stream1
and stream2
simultaneously so that the following becomes the result?
(1, 2)
(3, 4)
In other words, how could I modify combine_latest
in such a way that I can tell it downstream "hey, wait a second while I update the other streams before you emit your next value"?
I have found one possible answer, but it is not the best and I would like others.
I discovered the pausable
combinator. By passing in a stream that emits True or False, you can control if a sequence will be paused. Here is a modification of my example:
import rx
stream1 = rx.subjects.BehaviorSubject(1)
stream2 = rx.subjects.BehaviorSubject(2)
pauser = rx.subjects.BehaviorSubject(True)
rx.Observable\
.combine_latest(stream1, stream2, lambda x, y: (x, y))\
.pausable(pauser)\
.subscribe(print)
# Begin updating simultaneously
pauser.on_next(False)
stream1.on_next(3)
stream2.on_next(4)
# Update done, resume combined stream
pauser.on_next(True)
# Prints:
# (1, 2)
# (3, 4)
To apply to my GUI, I can create a BehaviorSubject
called updating
in my model that emits whether or not the whole model is being updated. For example, if stream1
and stream2
are being simultaneously updated, then I can set updating
to True. On any visualizations that are expensive to produce, I can apply the value of updating
to pause the combined stream.
This works in Rx for c#:
var throttled = source.Publish(hot => hot.Buffer(() => hot.Throttle(dueTime));
The dueTime
value here is a TimeSpan
in .NET. It merely says what the window of time is that you want to have inactivity before a value it produced. This basically gobbles up values produced "simultaneously" within a margin of time.
The source
in this case would be your .combine_latest(...)
observable.
来源:https://stackoverflow.com/questions/32104268/combining-observables-when-both-change-simultaneously