I see that CompletableFuture has a method handle that is the same as that of scala Future's handle basically converting success and exceptions all to success to be map and flatMap upstream(or thenApply and thenCompose in java world).
What is the equivalent of twitter future rescue(or scala future recoverWith) in java though?
rescue in scala is basically like the old java try....catch, then rethrow with more information so it can be nice to use. For example in twitterFuture.handle or scalaFuture.recover the return unit is U so you return a response. In twitterFuture.rescue or scalaFuture.recoverWith, it returns Future[U] so one can take certain exceptions, add more info and return Future.exception(xxxxx)
For recover, if you don't need to return a superclass and want to swallow all exceptions, you could just use exceptionally:
CompletableFuture<T> future = ...;
CompletableFuture<T> newFuture = future.exceptionally(_exc -> defaultValue);
Otherwise, you need to use handle to get a CompletableFuture<CompletableFuture<U>>, and then use thenCompose to collapse it:
CompletableFuture<T> future = ...;
CompletableFuture<T> newFuture = future.handle((v, e) -> {
if (e == null) {
return CompletableFuture.completedFuture(v);
} else {
// the real recoverWith part
return applyFutureOnTheException(e);
}
}).thenCompose(Function.identity());
Related
An answer here quotes a table of all CompletableFuture methods, but it's not quite what I'm looking for, or perhaps I'm going about it wrong.
I'm looking for the CompletableFuture equivalent of Streams' peek(), so basically a thenApply that returns the input argument or a thenRun that doesn't return Void. There are two ways I can think of that both don't semantically accurately express my intention, but do the job:
(..)
.thenApply((a) -> {
doSomething();
return a;
}
(..)
and
(..)
.whenComplete((result, exception) -> {
if (exception == null) {
doSomething();
}
}
(..)
Both take the input from the previous stage, allow me to perform an action and return with the same type to the next stage. Of these two the latter second approach limits my timing to when everything else is done, rather than async as soon as the previous necessary stage is complete.
I've also been searching for an identity function that takes a consumer function as argument, but it seems I would have to write that myself:
public static <T> Function<T, T> identityConsumer(Consumer<T> c) {
return a -> { c.accept(a); return a; };
}
(..)
.thenApply(identityConsumer(a -> doSomething()));
(..)
So short of writing my own util functions, is there an elegant way of performing an action in an intermediate stage, where I don't have to return something, while keeping the stage's current type?
Unlike with Stream, you can make multiple function chains from a single future, so it is not necessary to do everything in a single chain of calls. I would approach your problem like so:
var f1 = CompletableFuture.supplyAsync(...);
f1.thenRun(() -> doSomething()); // this is your "peek"
var f2 = f1.thenApply(...); // continue your chain of operations here
I wanted to do this too, so I just made my own wrapper:
/** Converts a Consumer into an identity Function that passes through the input */
public static <T> Function<T, T> peek(Consumer<T> fn) {
return (t) -> {
fn.accept(t);
return t;
};
}
used like:
.thenApply(Functions.peek(SomeUtil::yourConsumerMethod));
I get the feeling that I am misusing the CompletableFuture API.
When invoking CompletableFuture.exceptionally() I routinely find myself needing to invoke another asynchronous process, which means that exceptionally() returns CompletableFuture<CompletableFuture<T>> instead of CompletableFuture<T>. I then cast the result back using thenCompose(x -> x).
Here is a concrete example:
CompletableFuture<Void> listenersNotified = CompletableFuture.supplyAsync(() ->
{
int result = expensiveOperation();
List<CompletionStage<Void>> futures = new ArrayList<>();
for (EventListener listener: listeners)
listener.onSuccess(result);
return futures;
}).thenCompose(futures -> CompletableFuture.allOf(futures)).
exceptionally((exception) ->
{
List<CompletionStage<Void>> futures = new ArrayList<>();
for (EventListener listener: listeners)
futures.add(listener.onError(result));
return CompletableFuture.allOf(futures);
}).thenCompose(x -> x);
I understand that in the above example, one can return futures from inside exceptionally() and move thenCompose() after exceptionally() and this will work, but in real-life I don't always want to apply the same function to the result of thenSupply() as the result of exceptionally(). I want each section to take care of converting its own return type from a CompletableFuture to a synchronous value.
Is there a way to avoid falling into this pattern?
In my app I have 3 future calls, that are done in parallel and when a response for one of them is received, I have another 3 requests that all should finish before proceeding with code execution, precisely the DeferredResult from spring.
After a while, I realized that page is sometimes rendered before the latter 3 requests are done. Original source code (ommited logic for simplicity):
public DeferredResult<String> someControllerMethod() {
DeferredResult<String> result = new DeferredResult();
CompletableFuture.allOf(
future1(),
future2(),
future3()
)
.whenComplete((aVoid, throwable) -> result.setResult("something"));
return result;
}
public CompletableFuture<?> future3() {
return someService.asyncCall()
.thenApplyAsync(response -> {
....
return CompletableFuture.allOf(
future4(),
future5(),
future6()
);
}
);
}
With thenApplyAsync sometimes DeferredResult is completed before actual future, while changing to thenComposeAsync seems solve the issue. Could someone explain me why? Or it's a bug in my code somewhere and it should not behave this way?
thenApply[Async] accepts a function that evaluates to an arbitrary value. Once the value has been returned, the future will be completed with that value. When the function, like in your code, returns another future, this doesn’t add an additional meaning to it, the future will be the result value, whether completed or not, just like any other object.
In fact, your
public CompletableFuture<Void> future3() {
return someService.asyncCall()
.thenApplyAsync(response -> {
....
return CompletableFuture.allOf(
future4(),
future5(),
future6()
);
}
);
}
method does not even compile, as the result is CompletableFuture<CompletableFuture<Void>>, a future whose result value is another future. The only way not to spot the error, is to use a broader type, e.g. CompletableFuture<Object> or CompletableFuture<?>, as the return type of future3().
In contrast, thenCompose[Async] expects a function that evaluates to another future, to exactly the outcome you expect. That’s the fundamental different between “apply” and “compose”. If you keep the specific CompletableFuture<Void> return type for future3(), the compiler already guides you to use “compose”, as only that will be accepted.
public CompletableFuture<Void> future3() {
return someService.asyncCall()
.thenComposeAsync(response -> {
....
return CompletableFuture.allOf(
future4(),
future5(),
future6()
);
}
);
}
I would like to know if a one-liner exists for creating a CompletableFuture from a synchron method call. If no, why?
Long version:
final CompletableFuture<ReturnType> future = new CompletableFuture<>();
final String parameters = "hello";
ReturnType result;
try {
result = syncMethodCall(parameters);
} catch (Exception e) {
future.completeExceptionally(e);
}
future.complete(result);
return future;
Short desired version (or kind):
final String parameters = "hello";
return CompletableFuture.superMethod(() -> {syncMethodCall(parameters)});
Since you accepted an answer that performs an asynchronous call, it’s unclear why you asked for a “synchron method call” in the first place. The task of performing an asynchronous method invocation is quite easy with CompletableFuture:
String parameters="hello";
return CompletableFuture.supplyAsync(() -> syncMethodCall(parameters));
If your intention was to enforce the future to be already completed upon returning, it’s easy to enforce:
String parameters="hello";
CompletableFuture<ReturnType> f = CompletableFuture.supplyAsync(
() -> syncMethodCall(parameters));
f.handle((x,y) -> null).join();
return f;
The handle stage before the join ensures that in case syncMethodCall threw an exception, join won’t, as that seems to be your intention. But the handle stage is not returned, instead, the original future with the recorded exception will be returned.
Note that there’s a trick to do everything within the caller’s thread with the current implementation:
return CompletableFuture.completedFuture("hello")
.thenApply(parameters -> syncMethodCall(parameters));
The function passed to thenApply will be evaluated immediately when the future is already completed. But still, exceptions thrown by syncMethodCall are recorded in the returned future. So the outcome is identical to the “long version” of your question.
Since you want that your CompletableFuture is completed with a result of some method call, and you do not want to complete that CompletableFuture yourself - then you need not CompletableFuture - any Future implementation would be ok.
For example,
T function(parameters) {
return new T();
}
T res1 = function(parameters); // sync call
Future<T> f = ForkJoinPool.commonPool.submit(() -> function(parameters)); // async call
T res2 = f.get();
assert(res1.equals(res2));
I don't see an obvious way to handle an exception with an asynchronous result.
For example, if I want to retry an async operation, I would expect something like this:
CompletionStage<String> cf = askPong("cause error").handleAsync((x, t) -> {
if (t != null) {
return askPong("Ping");
} else {
return x;
}
});
Where askPong asks an actor:
public CompletionStage<String> askPong(String message){
Future sFuture = ask(actorRef, message, 1000);
final CompletionStage<String> cs = toJava(sFuture);
return cs;
}
However handleAsync doesn't do what you think it does - it runs the callbacks on another thread asynchronously. Returning a CompletionStage here is not correct.
Jeopardy question of the day: thenApply is to thenCompose as exceptionally is to what?
Is this what you are looking for?
askPong("cause error")
.handle( (pong, ex) -> ex == null
? CompletableFuture.completedFuture(pong)
: askPong("Ping")
).thenCompose(x -> x);
Also, do not use the ...Async methods unless you intend for the body of the supplied function to be executed asynchronously. So when you do something like
.handleAsync((x, t) -> {
if (t != null) {
return askPong("Ping");
} else {
return x;
})
You are asking for the if-then-else to be run in a separate thread. Since askPong returns a CompletableFuture, there's probably no reason to run it asynchronously.
Jeopardy question of the day: thenApply is to thenCompose as exceptionally is to what?
I know this was initially java-8, but, since java-12, the answer would be exceptionallyCompose:
exceptionallyCompose[Async](Function<Throwable,? extends CompletionStage<T>> fn [, Executor executor])
Returns a new CompletionStage that, when this stage completes exceptionally, is composed using the results of the supplied function applied to this stage's exception.
As the JavaDoc indicates, the default implementation is:
return handle((r, ex) -> (ex == null)
? this
: fn.apply(ex))
.thenCompose(Function.identity());
That is, using handle() to call the fallback, and thenCompose() to unwrap the resulting nested CompletableFuture<CompletableFuture<T>> – i.e., what you would have done in previous versions of Java (like in Misha’s answer), except you would have to replace this with completedFuture(r).
After a lot of frustration in trying to figure out the proper way of doing Scala's recoverWith in Java 8, I ended up just writing my own. I still don't know if this is the best approach, but I created something like:
public RecoveryChainAsync<T> recoverWith(Function<Throwable,
CompletableFuture<T>> fn);
With repeated calls to recoverWith, I queue up the functions inside the recovery chain and implement the recovery flow myself with "handle". RecoveryChainAsync.getCompletableFuture() then returns a representative CompletableFuture for the entire chain. Hope this helps.