I want to use reactor achieve:
for (val worker : getWorkers(request)) {
val response = worker.tryDo(work);
if (response != null) {
return response;
}
}
return null;
getWorkers can transfer to return Flux<Worker>, tryDo can also return a mono.
The key is I want exactly one or zero Response and only try the next if the current worker.tryDo fails.
Which operator do I need? I cannot find an answer in the document.
assuming you can rework tryWork to return a Mono that is empty when no work instead of returning null, you could use getWorkers(request).flatMap(worker -> worker.tryDo(work), 1).next()
the 1 parameter to flatMap instruct it to only consider workers 1 by 1.
workers that return an empty mono are effectively not influencing the output of flatMap.
Flux.next() converts to Mono by discarding elements past the first one and cancelling the source.
I found an answer in the gitter, which come from #OlegDokuka:
Mono.fromDirect(workersFlux.concatMap(worker ->
Mono.justOrEmpty(worker.tryDo(work))).take(1))
Update:
Thanks for #Simon Baslé: Use singleOrEmpty instead of fromDirect.
workersFlux.concatMap(worker -> Mono.justOrEmpty(worker.tryDo(work))).take(1).singleOrEmpty()
Related
I want to perform two business operations, in a Webflux environment, in a way that the second operation happens only after the first one is succesfull. After the second one is done, I want to return the result of the first operation. The second operation calls a org.springframework.web.reactive.function.client.WebClient. This is what I have tried until now:
public Mono<ResponseEntity<Resource>> callOperations(){
return service.operation1()
.flatMap(resource -> {
service.operation2();
return resource;
})
.map(ResponseEntity::ok);
}
I also tried with then and subscribe but I can't get the webclient to perform the call and return the result of service.operation1. What must I do?
You need to construct a flow using reactive operators and let WebFlux subscribe to it. In your snippet there is no subscription to service.operation2()
public Mono<ResponseEntity<Resource>> callOperations(){
return service.operation1()
.flatMap(resource -> {
return service.operation2()
.thenReturn(resource);
})
...
}
Is there any way to create a Flux by merging multiple Mono, the merged Mono is reading the value of the previous response.
Something like that :
Flux<Integer> values = Mono.just(1).mergeWith( value -> Mono.just(value * 2));
You have flatMapIterable/flatMapMany that could help, for ex:
.flatMapIterable(v -> List.of(v, v*2))
For posterity and to illustrate sp00m answer, here is an example of how I use this technique in combination with recursion to call Youtube API and navigate throw all the response pages.
The next page id is given in the current page in the getNextPagetoken()
public Flux<VideoListResponse> getvideoListResponses() {
Videos.List request = ...
return videoListResponseFluxPaginated(request, null, true);
}
private Flux<VideoListResponse> videoListResponseFluxPaginated(Videos.List request, String pageToken, boolean firstCall) {
if(pageToken == null && !firstCall) return Flux.empty();
request.setPageToken(pageToken);
return Mono.fromCallable(request::execute)
.flatMapMany(videoListResponse -> Flux.merge(Flux.just(videoListResponse), videoListResponseFluxPaginated(request, videoListResponse.getNextPageToken(),false)))
;
}
I have one code snippet, which is calling 2 different services based on some a if condition. And both the services return CompletableFuture<Optional<SomeObject>>. Following is the code logic looks like
if(someCondition){
CompletableFuture<Optional<SomeObjectType1>> = service1.call();
}else{
CompletableFuture<Optional<SomeObjectType2>> = service2.call();
}
And both SomeObjectType1 and SomeObjectType2 have a String inside it, which is of my interest. My current code looks like this:
private ContentWrapper getContentWrapper(input1, input2, ....) {
String content = null;
if (some_condition is true) {
List<Object_Type_1> list = service1.fetchTheCompletableFuture(..... inputs...)
.join()
.map(ListOutput::getList)
.orElse(null);
if (CollectionUtils.isNotEmpty(list)) {
content = list.get(0).getContent();
}
} else {
content = service2
.fetchTheCompletableFuture(..... inputs...)
.join()
.map(RenderedContent::getContent)
.orElse(null);
}
return content != null ? new ContentWrapper(content) : null;
}
Now my question is, can this if-else clause be removed or make it more clear by using lambdas. I am new in lambdas and does not have very good idea on this.
I am not sure whether the code below even compiles due to the vagueness.
private ContentWrapper getContentWrapper(input1, input2, ....) {
Optional<RenderedContent> content = some_condition
? service1
.fetchTheCompletableFuture(..... inputs...)
.join()
.map(ListOutput::getList)
.stream()
.findFirst()
: service2
.fetchTheCompletableFuture(..... inputs...)
.join();
}
return content
.map(RenderedContent::getContent)
.map(ContentWrapper::new).orElse(null);
}
The first service seems to yield a list of RenderedContent of which to take the first if there is one.
The second service may yield a Rendered content immediately.
So you can join the if-else to an Optional<RenderedContent>.
The map(RenderedContent::getContent) will yield Optional.empty() if it was empty to begin with. Otherwise getContent is called and wrapped in an Optional.
If present new ContentWrapper(...) might be called.
Notice much may fail, like getContent returning null (though there is an Optional.ofNullable.
Nevertheless Streams may be very expressive.
I would avoid using null in favor of Optional as that plays better together.
I am new to reactive programming and I want to transform the following code into non blocking way.
For the sake of simplicity, I created a sample pseudo code based from my original code. Any help will be appreciated.
public Mono<Response> getResponse(List<Provider> providers) {
for (Provider provider : providers) {
Response response = provider.invokeHttpCall().block();
if(response.getMessage() == "Success") {
return Mono.just(response);
}
continue;
}
return Mono.empty();
}
provider.invokeHttpCall() method
#Override
public Mono<Response> invokeHttpCall(){
WebClient webClient = WebClient.create();
return webClient.post()
.uri("/provider").accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Response.class);
}
I tried several tactics to implement this, but still no luck. Either all providers are invoked or I need to block the webclient thread.
Flux.fromIterable(providers)
.concatMap(Provider::invokeHttpCall) // ensures providers are called sequentially
.filter(response -> response.getMessage().equals("Success"))
.next()
reactive is a kind of Stream. Please think it as a Stream and program it reactively.
I give you such followed code.
Firstly, use Flux.fromIterable() to create a flux stream from a List.
Next, use flatmap() and Lambda fuction to emit the invoke into another new thread.
use method filterWhen() and Lambda to get the "Success" response and just get the first "Success" elements. See filterwhen api Doc.
Finally, just use Mono.from() to wrap the Flux and then return the Mono type.
public Mono<Response> getResponse(List<Provider> providers) {
return Mono.from(Flux.fromIterable(providers)
.flatmap(provider ->
Mono.defer(() -> provider.invokeHttpCall())
.filterWhen(response -> response.getMessage() == "Success");
}
if you want to see result and println().
Just use .subsribe() method to excute it.
getResponse.subsribe(System.out::println);
The doc says
Conceptually, it is a union of Single and Completable providing the
means to capture an emission pattern where there could be 0 or 1 item
or an error signalled by some reactive source.
But I am not sure what it truly means. It seems it is java8's Optional.
The following two codes have the same result , but I don't know what Maybe can do and Optional cannot (or cumbersome) do.
#Test
public void testMaybe1() {
Observable.just(3, 2, 1, 0, -1)
.map(i -> {
try {
int result = 6 / i;
return Maybe.just(result);
} catch (Exception e) {
return Maybe.empty();
}
})
.blockingForEach(maybe -> {
logger.info("result = {}", maybe.blockingGet());
}
);
}
#Test
public void testMaybe2() {
Observable.just(3, 2, 1, 0, -1)
.map(i -> {
try {
int result = 6 / i;
return Optional.of(result);
} catch (Exception e) {
return Optional.empty();
}
})
.blockingForEach(opt -> {
logger.info("result = {}", opt.orElse(null));
}
);
}
The results are the same :
result = 2
result = 3
result = 6
result = null
result = -6
In rxJava1 , My API used to return Observable<Optional<T>> , Is it a bad smell ? Should I change to Observable<Maybe<T>> ?
Maybe is a wrapper around an operation/event that may have either
A single result
No result
Error result
However Optional is a wrapper around a value that may either be
Present
Absent
In your example, in the map operation, the computation is synchronous (i.e. 6/i is synchronous and can result in a value immediately) and you want to propagate a value (if division is possible) or empty value (if division is not possible). Hence using Optional makes more sense.
There are however other options also:
If you want to propagate why division is not possible then you would want to report the exception that occurred. In such a case using Maybe will make more sense.
If you are not interested in both empty value and reason of error, then you simply want to skip propagating those results. In such a scenario I would use a flatMap instead of map. I will then not have to use any of Optional or Maybe.
.flatMap(i -> {
try {
int result = 6 / i;
return Observable.just(result);
} catch (Exception e) {
return Observable.empty();
}
})
Maybe is also useful when you have an Observable that can emit multiple values but you are interested in, let's say, only the first one and hence you use the firstElement() operator on the Observable. This returns a Maybe because either there is a single value, or there is no value (if source Observable does not emit any value before completing) or there is an error (if source Observable errors before emitting any value).
Maybe is a lazy stream of zero or one things (and being a stream can result in an error). Optional is not lazy, it it is either present or absent. There is no sense of deferred calculation with an Optional whereas there is with Maybe.
The difference relevant to your question is that Maybe can propagate error while Optional cannot - in your example one cannot distinguish between error and empty result. If error handling is important, Optional is useless, while Maybe has Maybe.error(Throwable). API-wise, for your use case I would prefer Single to Maybe - because it yields either Error or single Result, so return type would be Observable<Single<T>>
RxJava 2 targets Java 6. This means there is no builtin Optional support guaranteed, and they have to bring their own. Similar to how they have to bring their own Function types.
If your application/library only supports Java >= 8 you can use whatever suits you better.