Spring WebFlux: Merge Mono and Flux / Put Mono into Flux? - java

I'm wondering if it is possible to put the value of a Mono as a value into a Flux just like you could append any object to a list. I know there are some methods you could use but none of them fulfills my exact purpose. What I tried so far:
Flux<T> flux;
Mono<T> mono;
Flux.merge(flux, mono); // <- returns Flux<Object>
This doesn't sound too bad but notice that it does not return Flux<T> as I would need it but Flux<Object>. Same with Flux.concat(flux, mono);. The method Flux.zip(flux, mono); would stop merging as soon as the Mono completes as far as I understand.
Has somebody a solution for this?

This is what I ended up doing:
I have the method return a Flux of the desired type which in my case was an 'ExtendedResourceModel'. I create a Mono of this type which gets initialized by another method that I commented out to keep this answer as concise as possible. If a certain condition is met I want to also use a Flux from a different source so I use 'Flux.concat()' to put all elements into a single Flux. The 'concat'-method works by subscribing to the first argument, letting it finish, subscribing to the second one and so on.
In case of my condition not being met I just use 'concat' again but this time with only the Mono which will put the Mono's element into a new Flux. This way both cases have the same return type.

It is not possible to do, if you need to do that you can convert your Flux to mono
Mono monoToFlux = flux.collectList();
Mono mono;
Mono.zip (mono, monoToFlux).flatmap(tuple -> {
... more code ...
})

Related

RxJava return Observable after Observable.zip

I have two Observables which are API calls. I want these two to run in parallel and after they both finish I want to call a third observable with the combined result.
For example:
I have an Observable<List<Place>> getPlaces() and an Observable<AdditionalPlaceData> getAdditonalPlaceData()
My idea was a method that zips them then enriches the places with the additonal data and calls a third observable
I try it like this but the BiFunction of Observable.zip cant return an observable:
private Observable<List<Place>> getPlaces(){
return Observable.zip(getPlaces(), getAdditonalPlaceData(),(places, additonalPlaceData) -> {
//enrich places with additional data
return thirdApiCall(places);//This is not allowed
});
}
Is there an other way instead of Observable.zip I dont know to achieve this use case? And I dont want to use flatMap beacuse that would run the two observables in a sequence. Thanks for your help.

When to use .flatMap() on a Mono instead of just calling a function on a value?

I'm looking at some code where .flatMap was called on a Mono - I've gone through some of the PR discussions on Rx GitHubs about adding such functionality, but I don't really understand why someone would do that: flatMap, to me, seems inherently purposed for applying a function to 1...n elements. Applying a function to 0...1 elements can be done with a single function call.
You can use .map() as long as it is non-blocking.
Otherwise, your inner transformation will return Mono that will complete in future (e.g. a network call), and you should subscribe on it with .flatMap instead of blocking the processing.
flatMap works with any Publisher<T> and works with any 0..n where n can also be 0.
If you need to transform one element into a few (e.g. split a String) then you can use flatMapIterable, it has lower overhead than flatMap.

next() vs Single() Which one is effective?

I want cast to mono from flux. However, I can't decide when to use single() or next() and don't know which one is more effective?
Flux<String> optionalIdsFlux = Flux
.fromIterable(result.getPersonalizationEntity())
.filter(i -> i.getKey().equals(PERSONALIZATION_KEY))
.next() // or single() ??
.map(DataEntity::getValue)
.flatMapMany(Flux::fromIterable);`
They are actually quite different. next() takes the first value that is emitted and cancels the subscription afterwards.
single(), on the other hand, expects that exactly one element is emitted in the first place. If that's not the case, and zero or more elements are emitted, then an error signal is emitted.
Which one to choose depends on your use case. If the source is guaranteed to emit exactly one element, then you can use single(). Otherwise use next().
Additionally to what a better oliver said, if there is a super strong guarantee that the Publisher you have only ever emits at most one onNext, you can turn it into a Mono - without the safety belt on - by using Mono.fromDirect(Publisher).

How to perform a completable and then return a processed result with single?

I hope my question is clear enough.
Let's say your have an API that performs requests over a network backed by IO-streams (input and output).
Using RxJava (which I am debuting with), I would think it could be possible to do the following:
public Single<MyData> getMyDataFromApi() {
return requestMyData()
.map/flat/then()->waitAndprocessData() // here is the missing link for me. What can I use ?
.andThen()->returnData()
As you will understand, the method requestMyData returns a Completable which sole responsibility and only task it to perform said request (IO-type operation).
Then, upon performing the request, the remote entity shall process it and return a result the requested MyData object by performing an IO-type operation as well.
The key-point here, is that I work with streams (both input and output) which reading and writing operations are obviously performed in separate IO threads (using Schedulers.io()).
So in the end, is there a way so that my getMyDataFromApi() method does the following :
Perform the request -> it's a completable
Wait for the result -> something like a subscribe ? but without splitting the chain
Process the result -> it's a single or can be a lambada in map method
Return the result -> final element, obviously a single
To conclude, I strongly believe that requestMyData's signature should be that of a Single, because it's getter and I am expecting a result or an error.
Without having the implementation of the methods is quite hard to understand the real problem.
If requestMyData returns a Completable and waitAndprocessData a Single, you can do the following:
return requestMyData().andThen(waitAndprocessData());
Anyway remember that a Completable is computation without any value but only indication for completion (or exceptions).

RxJava infinite retry with new observable

I am using the Rx-ified API for vertx, and this question has to do a potentially infinite retry-until-success loop I would like to implement but am having difficulty. I'm new to RxJava.
Here's what I'd like to do:
Send a request to another vertx component using the vertx message
bus
For as long as I get a timeout waiting for a response, re-issue
the request
Once I have a response to the request, inspect the
results, and if there is nothing usable, wait for a period and then
start all over again at step 1)
The first problem
The first problem I encounter is how to accomplish step 2).
If you are familiar with the vert.x Rx api, this is what it means to issue the request in step 1) above:
vertx.eventBus().<JsonObject>sendObservable( "theAddress", aJsonObject );
The above code returns an Observable instance that will emit either the response, or an error (if there was a timeout, for example). That Observable will never emit anything ever again (or else it will always emit exactly the same thing every time something subscribes, I don't know which).
RxJava retry operator doesn't seem to work
vertx.eventBus().<JsonObject>sendObservable( "theAddress", aJsonObject )
.retry()
I thought that in order to issue the retry, I could just use RxJava's retry() operator, which I tried, but doing so has exactly no useful effect whatsoever, due to the nature of the source observable. No new request message gets sent, because the only thing that is being "retried" is the subscription to the original source, which is never going to emit anything different.
RxJava retryWhen operator doesn't seem to work
vertx.eventBus().<JsonObject>sendObservable( "theAddress", aJsonObject )
.retryWhen( error -> {
return _vertx.eventBus().<JsonObject>sendObservable( "theAddress", aJsonObject )
})
So then I thought I could use RxJava's retryWhen() operator, which allows me to issue a second observable when the root observable emits an error. The second observable could, I figured, just be the same code above that produced the initial observer in step 1.
But, the retryWhen() operator (see documentation) does not allow this second observable to emit an error without ending the subscription with an error.
So, I'm having trouble figuring out how to set up a potentially infinite retry loop within the first part of this chain.
I must be missing something here, but I have not been able to determine what it is.
The second problem
vertx.eventBus().<JsonObject>sendObservable( "theAddress", aJsonObject )
// imagine that retryWhen() accomplishes an infinite retry
.retryWhen( error -> {
return _vertx.eventBus().<JsonObject>sendObservable( ... )
})
.flatMap( response -> {
// inspect response, if it has usable data,
// return that data as an observable
return Observable.from(response.data());
// if the response has no usable data,
// wait for some time, then start the whole process
// all over again
return Observable.timer(timeToWait).<WHAT GOES HERE?>;
})
The second problem is how to implement step 3. This seems to me to be like the first problem, only harder to understand, because I don't need to retry the immediate source observable, I need to wait for a bit, then start over at step 1).
Whatever Observable I create would seem to require all of the elements in the chain leading up to this point, which seems like a kind of recursion that should probably be avoided.
Any help or suggestions would be really welcome at this point.
Many thanks to Ben Christensen over on the RxJava Google Group for pointing out the defer() operator, which will generate a new Observable on each subscription. This can then be composed with the standard retry() operator to get inifinite retry.
So, the simplest solution to the first problem in my question looks like so:
Observable.defer( () -> vertx.eventBus().<JsonObject>sendObservable( "theAddress", aJsonObject ) )
.retry()
...if exponential backoff is required, you can add Observable.timer() with appropriate parameters in the factory method given to the defer() operator.
I am still looking into the second problem.

Categories

Resources