Nice!

On Tue, Jul 22, 2014 at 6:09 PM, Mike Hearn <m...@plan99.net> wrote:
> I have what I imagine is a fairly typical JavaFX application (once it's
> released I'll post more about it). It has a GUI, some mostly asynchronous
> state management, and interactions with various servers that can change the
> apps state.
>
> At first I tried the simple and obvious approach in which the backend would
> schedule closures onto the UI thread using Platform.runLater() when things
> changed, sometimes via ad-hoc event handlers or callbacks. But it ended up
> getting more and more complicated and unclear. So I ended up rewriting
> things to be more actor-ish in which the backend code ran mostly in its own
> thread and vended "mirrored observables".
>
> I figured mirrored observables are a generally useful concept that probably
> JavaFX should have itself.
>
> The idea is simple enough. Given an ObservableList or ObservableSet (I
> didn't need map yet), calling a static utility function with that list and
> an Executor returns a new list in which all updates run in the context of
> that executor. This means a piece of code that's responding to changes in
> state held elsewhere e.g. via a network connection which receives updates
> from a server can have its own thread, and update its own
> ObservableLists/Sets/Maps without thinking about threading as long as the
> only public accessors for these collections vend mirrored versions. Note
> that mirrors are read-only, I don't attempt to do two-way sync (with
> conflict resolution?!). If you want to update the "real" list you have to
> schedule a closure onto the backend thread to do it and wait for the change
> to re-propagate to the frontend thread.
>
> Once this is in place, you can then bind the collections to UI controls
> using some extra transformers in the standard manner, and everything hangs
> together nicely.
>
> The code I'm using is here (Apache licensed, go wild)
>
>    https://gist.github.com/mikehearn/4781ce7f00228762adfb
>
> There are three files. AffinityExecutor is an extended version of the
> Executor interface which has a notion of thread ownership (supports short
> circuiting and assertions), along with static methods to create:
>
>    1. AffinityExecutors that are bound to a dedicated new thread with a
>    task queue.
>    2. An AffinityExecutor that queues up tasks but doesn't execute them
>    until explicitly requested, this is useful for unit testing.
>    3. An AffinityExecutor that wraps Platform.runLater and
>    Platform.isFxApplicationThread
>    4. An executor that just runs closures immediately on the same thread.
>
> Then ObservableMirrors creates sets/lists in the same way a content binding
> would, but which re-applies changes in the given thread or short-circuits
> and does so immediately if the listener on the mirror is running on the
> same thread as the caller.
>
> There's also a set of static addListener methods in MarshallingObservers
> that just relays into the right thread as well, if you only care about
> changes and not full content.

You can also use ReactFX streams to transfer events (here list
changes) to another thread:

    ObservableList<T> list = ...;
    EventStream<Change<? extends T>> changesOnFxThread =
            EventStreams.changesOf(list)
                    .threadBridgeToFx(backendExecutor);

See 
http://www.reactfx.org/javadoc/org/reactfx/EventStream.html#threadBridgeToFx-java.util.concurrent.Executor-

>
> Of course you could have an ObservableMirrors equivalent that uses a
> regular Java executor, you'd just lose some safety and short circuiting.

Reply via email to