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.
Related
I have a Flux.concat() operation which takes 3 checks like this:
public Mono<Boolean> checkSleep(Student std)
{
Flux.concat(isHealthy(std), isWealthy(std, sleep), isWise(std, sleep))
.filter(result -> !result)
.next()
.flatMap(result -> Mono.just(false)) //returns false if any one is false
.switchIfEmpty(Mono.just(true)); // returns true if all are true
}
each of this methods has a common external api call extService.getSleep(Student std) to get Sleep Object Mono<Sleep> for its flow.
I want to call extService.getSleep(Student std) only once in the entire flow,
ideally in the first check isHealthy(std) and pass the object Mono<Sleep> to the next 2 checks as well.
I am not understanding how to make this call as Flux.concat does not allow a Mono to be added in the prefix.
Each of the checks have similar body like this:
Mono<Boolean> isHealthy(Student std)
{
return Mono.just(std)
.flatMap(std->extService.getSleep(std)).map(sleep-> sleep.isValid());
}
in the next check I want to pass sleep object from previous method,
isWealthy(Student std, Sleep sleep)
I do not want to call extService.getSleep(std)) once again,
I thought of creating a variable outside these 3 methods and update it when the api returns a something,
it throws error saying "Variable used in lambda expression should be final or effectively final"
Let me know if there is a better way to handle this scenario.
I am new to reactive spring programming, any help is appreciated.
thanks in advance.
Your line of thinking was not far off!
Whenever you need to "reach outside" a publisher, consider using AtomicBoolean, AtomicInteger, etc. or the parameterized AtomicReference to get around the final or effectively final compiler warning. However, it should be noted that asynchronous operations like flatMap may not have the correct value when they get the wrapped values from these, so it's best to get around the problem in a different way.
Fortunately, Reactor has a myriad of useful methods on its publishers!
If I understand correctly, the checkSleep function should resolve to true if all three of isHealthy, isWealthy and isWise also resolve to true - false if even one of them resolve to false.
I have created a simple simulation of this scenario:
private Mono<Boolean> checkSleep(Student std) {
return getSleep(std)
.flatMapMany(sleep -> Flux.merge(isHealthy(std, sleep), isWealthy(std, sleep), isWise(std, sleep)))
.all(result -> result);
}
private Mono<Sleep> getSleep(Student std) {
return Mono.just(new Sleep(8));
}
private Mono<Boolean> isHealthy(Student std, Sleep sleep) {
return Mono.just(true);
}
private Mono<Boolean> isWealthy(Student std, Sleep sleep) {
return Mono.just(true);
}
private Mono<Boolean> isWise(Student std, Sleep sleep) {
return Mono.just(true);
}
This way, getSleep is only called once, and is used to flat map the emitted value into the three booleans you're looking for. The Flux::all method then ensures that the returned Mono will wrap true only if all three inners have emitted true.
Another thing to note is that I've replaced Flux::concat with Flux::merge. The former goes sequentially, subscribing, waiting for result, then repeat. These three publishers seem to be independent of one another, so replacing concat with merge allows all three to be subscribed to at the same time, thereby reducing time wasted with waiting.
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 ...
})
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).
Suppose I have a list of Flowable, and I would like to do something until all Flowable are finished, how can I do it?
Sample Code
List<Flowable<Model>> flowableList = getFlowableList();
List<List<Model>> results = blockUntilAllFlowablesAreFinished(flowableList); // is there a similar method?
... // do something with results
I'm not sure why you are thinking in a synchronous fashion but I believe you'll achieve your goal with something like:
Flowable.combineLatest(flowableList -> {
// do something with results
});
Of course, besides combineLatest, there are bunch of operators that allow you to "combine" flowables e.g. zip. Choose one that suit you here https://github.com/ReactiveX/RxJava/wiki/Combining-Observables
I think you can use Flowable.zip() with Iterable as parameter, it will emit when all Flowables in Iterable are done.
I feel like this is a dumb question, but I couldn't find any answer for a while, so I'm gonna ask it, sorry :)
So, I need a function that does the following:
1) Calls another function to create an Observable User
2) Gets the User object from the Observable User
3) Gets some info about the user and runs through some logic
4) Returns Observable User
I am having troubles with step #2. How do I do that? Or, is this approach somehow fundamentally wrong?
Here's the "model" of the function:
#Override protected Observable buildUseCaseObservable(){
Observable<User> userObservable = userRepository.findUserByUsername(username);
//User user = ??????
//if (...) {...}
return userObservable;
}
Thank you :)
You can use operators(map, flatMap, doOnNext, etc) to get the object wrapped by your observable through the pipeline
Observable.just("hello world")
.map(sentence-> sentence.toUpperCase) --> do whatever you need.
.subscribe(sentence -> println(sentence)
By design Observable follow the Observer patter, which subscribe to the observable and receive the item once has been emitted through the pipeline.
Also what you can do is instead use observer patter, just extract the object from the pipeline using toBlocking. But that´s is consider an anti pattern and means you´re not applying a good design.
#Test
public void observableEvolveAndReturnToStringValue() {
assertTrue(Observable.just(10)
.map(String::valueOf)
.toBlocking()
.single()
.equals("10"));
}
You can see more examples about to Blocking here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableToBlocking.java
You cannot 'extract' something from an observable. You get items from observable when you subscribe to them (if they emit any). Since the object you are returning is of type Observable, you can apply operators to transform your data to your linking. The most common and easy to use operator in RxJava is 'map' which changes one form of data to other by applying a function.
In your case, you can use 'map' operator directly on Observable<user>:
return userRepository.findUserByUsername(username)
.map(new Func1<User, Object>() {
#Override
public Object call(User u) {
// ..
// apply your logic here
// ..
return myDataObject; // return you data here to subcribers
}
});
I hope you know the basics of RxJava and doesn't need any introduction about how to use operators. For map documentation, follow this link