Why is it necessary to call `toBlocking` before `single`?

Viewed 1391

I'm exploring a Swagger-generated Java API. In the generated client file, I see two methods like this:

public Observable<UserAccountView> createUserAccount(/* params omitted  */) {
   /* body omitted */
}

public UserAccountView createUserAccountSynchronously(/* params omitted */) {
  return createUserAccount(/*params*/).toBlocking.single();
}

I've read the RxJava docs and related resources but since I'm new to this I still find myself thinking toBlocking shouldn't be needed here-- doesn't blocking/non-blocking lose its meaning for a Single? Is there such a thing as a blocking Single?

Clearly I'm not fully grasping the relationship between the two methods. I am thinking that single() is extracting a singular event from an observable before unsubscribing. Why should toBlocking be needed in order to do that?

1 Answers

The toBlocking() call is there so that the method returns UserAccountView instead of Observable<UserAccountView>. toBlocking() converts the Observable into a BlockingObservable, and calling BlockingObservable.single() returns whatever item is emitted by the BlockingObservable, as long as it only emits one item before completing (more or less than one results in an error being emitted).

If you were to call Observable.single() without converting it to a BlockingObservable in RxJava1, it would return an Observable<UserAccountView> that emits only a single item, if the Observable completes after emitting one item. In RxJava2, this API is improved so that the equivalent operator, Observable.singleOrError(), returns a Single<UserAccountView>.

Blocking/non-blocking does not lose its meaning for a Single (or in this case, an Observable that will only emit a single item). Depending on how you've set up your Schedulers (e.g. using subscribeOn(), observeOn() or an operator that uses a Scheduler), you can have a Single/Observable that only emits its one item asynchronously after doing processing in some worker thread. So, if the caller of createUserAccount() is running in the main thread of the application, the Observable<UserAccountView> could return immediately, and emit its result downstream on a worker thread after a few seconds, without the main thread ever being blocked. On the other hand, using createUserAccountSynchronously() will block the main thread until Observable<UserAccountView> emits its one item.

Generally in reactive applications you avoid using toBlocking(), but sometimes you need to bridge between the reactive world and the non-reactive, blocking world, and that's where toBlocking() can be useful.

Related