0

I'm new to RxJava. I have a few Jersey RxJava clients that return Observables. I need to make one call to get some data, that data becomes the input to my next 3 calls. I want those calls to be made in parallel. Finally, I want to do a calculation once all the calls have completed that requires all the data. Here is how it looks:

interface Service {
  Observable<ResultA> callServiceA(InitialData input);
  Observable<ResultB> callServiceB(ResultA resultA);
  Observable<ResultC> callServiceC(ResultA resultA);
  Observable<ResultD> callServiceD(ResultA resultA);
  FinalResult simpleCalculation(ResultA a, ResultB b, ResultC c, ResultD d);
}

class MyClass{

   @Autowired
   ExecutorService myExecutorService;

   Observable<FinalResult> myMethod(InitialData initialData){
   /* Make call to ServiceA, get the results, then make calls to services B, C, and D in parallel (on different threads), finally perform simpleCalculation, and emit the result */
   }
}
Adam
  • 43,763
  • 16
  • 104
  • 144

2 Answers2

4

flatMap() and zip() are your friends in this situation.

Observable<FinalResult> myMethod(InitialData initialData) {
    return service
            .callServiceA(initialData)
            .flatMap(resultA -> Observable.zip(
                    service.callServiceB(resultA),
                    service.callServiceC(resultA),
                    service.callServiceD(resultA),
                    (resultB, resultC, resultD) -> 
                      service.simpleCalculation(resultA, resultB, resultC, resultD))
            );
}

Using the return observable will look like this:

Subscription subscription =
        myMethod(new InitialData())
                .subscribe(finalResult -> {
                            // FinalResult will end up here.
                        },
                        throwable -> {
                            // Handle all errors here.
                        });
kjones
  • 5,783
  • 2
  • 27
  • 27
  • Thanks! How can I make it call services B, C, and D in parallel on different threads? Don't I need to add a scheduler? – Adam Jul 28 '16 at 00:38
  • So many people are using Retrofit for their network calls which will automatically schedule calls in parallel. If your service is executing these synchronously, then add .subscribeOn(Schedulers.io()) to each call. – kjones Jul 28 '16 at 00:47
  • I'm not using Retrofit. I'm using Jersey Client. In the past I've added .observeOn(Schedulers.from(executorService)) to the end of the chain. How does that differ from .subscribeOn? – Adam Jul 28 '16 at 00:58
  • I don't fully understand the implications of Jersey Client. Here is their link for async Rx https://jersey.java.net/documentation/latest/rx-client.html#rx.client.rxjava.usage – kjones Jul 28 '16 at 01:01
  • When you subscribe to an Observable it will be executed on the current thread unless a .subscribeOn() scheduler has already been added to the Observable. If your Observable is synchronous, using only .observeOn(Schedulers.from(executorService)) will execute the network request on the current thread and then send the result to the executorService. Using .subscribeOn(Schedulers.from(executorService)) will release current thread when subscribe and initiate the request using the executorService. – kjones Jul 28 '16 at 01:07
  • Hopefully, the jersey client allows some way to setup async execution of all calls. Worst case if for some reason all Jersey Client Observables are synchronous, would be to add a layer above the jersey client that transforms each of their synchronous Observables into asynchronous Observables. – kjones Jul 28 '16 at 01:14
0

You can use flatMap to do a synchronous call, then use zip or merge to send out multiple calls, then flatMap that again when complete.

Graeme
  • 1,107
  • 2
  • 12
  • 30