How to include conditions in Rx chains? - java

I am trying to switch to another observable if a certain condition is met. Here, if the list is empty, I need to use another observable:
Observable<List<Something>> obs = service.getObservable();
obs.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.ifTheListIsEmpty(()-> newObservable) // I need something like this
.subscribe(subscriber)
I read about switchCase in this extension but I didn't get how to use it...

Use flatMap:
.flatMap(list ->
list.isEmpty()
? otherObservable
: Observable.just(list))

Although #Tassos Bassoukos has a valid answer, another way to do this is:
obs.flatMapIterable(l -> l)
.switchIfEmpty(otherObservable)
Depending on your needs, you might consider an Observable<Something> that emits multiple Something. It allows you to create obs using Observable.from(list). You don't even need flatMapIterable anymore!
It also makes usage of other operators like map much easier since you do not have to operate on the entire list anymore.

Related

How to use CompletableFuture for making two DB call and wait for the result

Suppose I have a situation where I call two Repositories to get items.
CompletableFuture<Optional<Account>> accountCompleteableFuture = CompletableFuture.supplyAsync(() ->AccountRepository.findById(accountId));
CompletableFuture<Optional<User>> userCompletableFuture = CompletableFuture.supplyAsync(() ->userRepository.findById(userId));
How Can I get a feedback when both are done brining back the results ? Traditionally I would call both one by one and then do the remaining logic. Here I want to speed up the process . I was trying to do something like thenCombine() ,but both are different Objects and I cannot write the logic in inside that lambda. Can any one suggest better way to do it?
CompletableFuture.allOf( groupCompletableFuture, userCompletableFuture ).join();

Extending Rx Singles zip infinitely

I need to make a lot of API calls asynchronously and obviously make sure that they all pass so I can handle error/success in a single place.
Is there a way to easily extend Singles.zip() functionality in Kotlin to take more than base 9 parameters (in best case scenario, to make it take any given number of parameters) without doing massive amounts of copy-paste work when writing your own extensions for t10, t11, etc. ?
Well, writing dozens of extensions simply works, but is cumbersome, not very elegant and adds additional work.
Single.zip method takes an Iterable of Singles
val list = arrayListOf<Single<String>>()
list.add(Single.just("hello"))
list.add(Single.just("world"))
Single.zip(list) { args -> Arrays.asList(args) }
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.subscribe()

Should I use "then" or "flatMap" for control flow?

So, I'm trying to work with Webflux and I've got a scenario "check if an object exists; if so, do stuff, else - indicate error".
That can be written in reactor as:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id){
return id.
flatMap(repository::exists). //repository.exists returns Mono<Boolean>
flatMap(e -> e ? e : Mono.error(new DoesntExistException())).
then(
//can be replaced with just(someBusinessLogic())
Mono.fromCallable(this::someBusinessLogic)
);
}
or as:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id){
return id.
flatMap(repository::exists). //repository.exists returns Mono<Boolean>
flatMap(e -> e ? e : Mono.error(new DoesntExistException())).
map(e -> this.someBusinessLogic()));
}
Let's assume that return type of someBusinessLogic cannot be changed and it has to be simple void, not Mono<Void>.
In both cases if the object won't exist, appropriate Mono.error(...) will be produced.
While I understand that then and flatMap have different semantics, effectively I get the same result. Even though in second case I'm using flatMap against its meaning, I get to skip flatMap and fromCallable in favor of simple map with ignored argument (that seems more readable). My point is, both apporaches have advantages and disadvantages when it comes to readability and code quality.
So, here's a summary:
Using then
pros
is semantically correct
cons
in many cases (like above) requires wrapping in ad-hoc Mono/Flux
Using flatMap
pros
simplifies continued "happy scenario" code
cons
is semantically incorrect
What are other pros/cons of both approaches? What should I take under consideration when choosing an operator?
I've found this reactor issue that states that there is not real difference in speed.
TL, DR: If you care about the result of the previous computation, you can use map(), flatMap() or other map variant. Otherwise, if you just want the previous stream finished, use then().
You can see a detailed log of execution for yourself, by placing an .log() call in both methods:
public Mono<Void> handleObjectWithSomeId(Mono<IdType> id) {
return id.log()
.flatMap(...)
...;
}
Like all other operations in Project Reactor, the semantics for then() and flatMap() are already defined. The context mostly defines how these operators should work together to solve your problem.
Let's consider the context you provided in the question. What flatMap() does is, whenever it gets an event, it executes the mapping function asynchronously.
Since we have a Mono<> after the last flatMap() in the question, it will provide the result of previous single computation, which we ignore. Note that if we had a Flux<> instead, the computation would be done for every element.
On the other hand, then() doesn't care about the preceding sequence of events. It just cares about the completion event:
That's why, in your example it doesn't matter very much which one you use. However, in other contexts you might choose accordingly.
You might also find the Which operator do I need? section Project Reactor Reference helpful.

RxJava: Is it possible to avoid toBlocking().single()?

I sometimes use RxJava to write in a more functional style for some complex filterings, mappings and so on (I know that this is not what it's made for, and I would be happy to use Kotlin or Java 8 for that, but I can't (I'm on Android so, stuck to 6).
However, when you try to "extract" the actual objects out of the Observable, you always need to write .toList().toBlocking().singe()
E.g. (not so complex, but you get the idea):
final List<CashRegister> cashRegisters = cashRegisterData.getCashRegisters();
return Observable.from(cashRegisters)
.map(CashRegister::getName)
.toList()
.toBlocking()
.single();
As you can see, "converting" the Observable into a list needs more lines than the actual mapping I want to perform.
Is there a way of doing this without the toList().toBlocking().single() chain?
You can use IxJava to perform the same operation:
final List<CashRegister> cashRegisters = cashRegisterData.getCashRegisters();
return ix.Ix.from(cashRegisters)
.map(CashRegister::getName)
.toList();
With RxJava one has to change the way they think about using Observables.
The observable operation chain should be treated more like a math formula, not a sequence of operations. So, instead of trying to get results out of observable, try to figure out what you want to do with that list.
In RxJava, prefer not to extract the observable values, but to subscribe to them:
someObservable.subscribe(result -> {
// Now do your deed with the result
// for example:
someAdapter.setData(result);
someAdapter.notifyDatasetChanged();
})
I'd kindly recommend to read the documentation on How to use RxJava to understand the idea.

Rx Java subscribe only to one item emitted

I have a BehaviorSubject data that contains actual data (or maybe nothing, if nothing has been emitted to it). I want to subscribe for only one item it emits, i.e. either the current observed value or the first to be passed to it from somewhere else. I'm currently doing it the following way:
Subscription firstItemSubscription = data.subscribe(item -> {
firstItemSubscription.unsubscribe();
processItem(item);
});
Is there any operator or transformer that I could use instead? Or probably there is completely different, more Rx approach that would allow me to do the thing I want to?
Yes use just need to use take(1)
Observable observable = //some observable
observable.take(1).subscribe(/* do your thing */);

Categories