Incremental updates from Tasks
roman at kennke.org
Fri Jan 6 13:28:24 PST 2012
> >> OK, here is the next issue I ran into while documenting the Task for the last issue. We don't really have a good way for the Task to do incremental updates to the Task value. Instead, it is only set at the conclusion of the call method, and only when the Task isn't cancelled. This is fairly limiting. Instead, it would be nice to allow Task subclasses to set the value whenever they like -- before execution (such as in the constructor), multiple times during execution, or even after it has been cancelled.
> >> This is the issue: http://javafx-jira.kenai.com/browse/RT-18820
> > I was thinking about the same when you asked about how to get the state
> > (thread safe) into a cancelled() callback method. An API to do partial
> > updates would be helpful.
> > What about an API similar to how it's done in SwingWorker? There we
> > define a 2nd type parameter T and publish() partial results. This would
> > be called from the call() method whenever we have a partial result:
> > protected void publish(T chunk)
> > Which in turn results into a call (on the JavaFX thread) to:
> > protected void process(List<T> chunks...)
> > Which allows to process one or more chunks that have been published
> > before using publish().
> > I like that API and it solves the question how to handle atomic vs. list
> > types simply by introducing a new generic type. It also has the
> > advantage that Swing refugees feel familiar with it :-)
> > Anything wrong with that approach?
> There are two things I don't like about that approach. The first is that it introduces overhead in cases where we are only interested in the "most recent intermediate value". That "chunks" list might be 10,000 elements long, and we only care about the very last item in it.
The chunks list is usually short. Whenever the background thread calls
publish() the item is put into the chunks list, as soon as the EDT gets
to run (hopefully often enough!) whatever is in that list gets
process()ed. The next publish() starts in a fresh list. In other words,
we only get the latest bunch of data. Whether the implementation keeps
all the data around or not is up to itself.
> The second thing I don't like about it is that for cases where you are computing an atomic value, it is more complicated. You have to call publish and implement process. With the proposed updateValue, you simply call updateValue from your Task implementation and the rest is just handled for you.
Hmm yes. If we do a generic Task class and specific subclasses for lists
or atomics, the atomic version could by default have the same V and T
and call updateValue() in its implementation of process()
(implementation could optionally override it to do its own processing).
> The Swing solution here I think is quite flexible -- and that is both its strength and weakness. Although this might very well be what I should use for the observable list case.
> I guess it comes down to a trade-off. Do we have a single class that handles incremental updates to atomic & collection values (reduced number of classes, less "cluttered" namespace, worse performance for updates to atomic values that are frequently updated), or do we split this up into multiple classes (more classes, better performance, more situation-specific API)?
> I do remember feeling that publish / process was awkward to use back when I was learning it, and I didn't spend enough years with it for it to become natural I guess :-)
I'd think that it would be good to have a generic and superflexible
class (maybe based on publish/process like API) and at least 2 specific
subclasses, for atomic and for lists. I already outlined how an atomic
version could work above. A list version would have List<T> as V and T
for partial values, and maybe also do some default stuff in process()
(like updateProgress() ).
More information about the openjfx-dev