2

I recently came across the ReactiveX pattern for asynchronous data-flows. I studied the information provided there and also watched this talk by a Netflix engineer on how they used Observables to redesign their web API architecture.

ReactiveX certainly seems like a clean and powerful abstraction, but I don't see the difference between an Observable<T> and a Iterable<Future<T>>. In the first case, I could do something as follows:

Observable<T> observable = getData()
observable.subscribe(onSuccess, onError, onCompletion)

And in the second case, I would do

Iterable<Future<T>> futures = getData()
futures.forEach(future => future.onSuccess(onSuccess), future.onError(onError))

where onSuccess, onError, and onCompletion are some functions I have defined elsewhere.

What advantages does the first example have over the second?

P.S. This is just out of curiosity. I currently use Python (and JavaScript) in my job, so I won't be using the ReactiveX paradigm anytime soon.

  • From your example, it would appear that Future<T> is a kind of Observable<T>. So really the difference seems to be subscribing to a single observable versus subscribing to a collection of observables. – Ben Cottrell May 7 '16 at 15:33
  • Hi. Check out official's page example. Reactive Programming. Reactive Progamming block. It says what @Giorgio is explaining – Laiv May 7 '16 at 17:20
3

Your examples are not semantically equivalent:

Observable<T> observable = getData()
observable.subscribe(onSuccess, onError, onCompletion)

will execute the onSuccess, onError procedures each time a new element is received.

On the other hand, with

Iterable<Future<T>> futures = getData()
futures.forEach(future => future.onSuccess(onSuccess), future.onError(onError))

the iterable delivers all the futures, and you register callbacks on each future as you get it from the iterable. But, at this point, the futures may still be running. Later, these futures may complete in any order, not necessarily in the order in which they were provided by the iterable.

Also, the first solution can received a possibly unlimited stream of data without blocking: each time a new element arrives, a callback is invoked. The second solution can't: it only makes sense if you want to start a finite number of asynchronous computations.

To have a better intuition, take a look at the table on this page or this one (thanks to @Laiv for the link, I was not able to find it back).

  • In your first solution you have multiple values that are asynchronously pushed to your program (bottom right of the table).

  • In your second solution you have multiple value that are synchronously pulled by your program. In turn, each one of these values, is a computation that asynchronously pushes a single value.

An example of what you can do with the first solution is the following. Suppose that you have an observable representing all GUI events in an application:

Observable<Event> events = ...

Now if you want to listen only on the events of a given button that is, say, identified by its label "HelpButton", you can write the observable:

Observable<Event> helpButtonEvents =
    events.filter(e => "HelpButton".equals(e.getName()))

helpButtonEvents.subscribe(onHelpInvoked, ..., ...)

It should be clear that you cannot use an iterable of future here, because you do not know how many GUI events you will get and therefore you would need to have an iterator polling on events forever.

  • Yes, I've seen that chart, and I like it a lot. But nowhere in that chart do we see Iterable<Future<T>>. Only Iterable<T> and Future<T>. So I don't believe it answers my question. Regarding: "In your second solution you have multiple value that are synchronously pulled by your program." Sure they are synchronously pulled, but they are Futures, so they are pulled immediately. What's important is that the values the Futures contain are themselves asynchronously pushed. – gardenhead May 7 '16 at 18:47
  • "But nowhere in that chart do we see Iterable<Future<T>>": It is not there because Iterable<Future<T>> is a composite of Iterable<S> (pull of multiple values) where each value is of type S = Future<T> (push of a single value). So you first pull several future-values, and then its future pushes its own value. – Giorgio May 7 '16 at 19:04
  • "What's important is that the values the Futures contain are themselves asynchronously pushed.": With this solution you cannot receive an infinite sequence of values asynchronously. With the first you can. – Giorgio May 7 '16 at 19:05
  • OK, I agree that you cannot receive an infinite number of events with an Iterable. More accurately, you must set the number of events before you can know how many events will actually be triggered. That's an important difference, but I wonder if there's any others. – gardenhead May 7 '16 at 19:23
  • @gardenhead: Another difference is that the order in which your futures are going to respond may be different from the order in which they were created. Is this what you want? – Giorgio May 7 '16 at 20:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.