Why is CompletableFuture.allOf declared as CompletableFuture<Void>? - java

Why is CompletableFuture.allOf declared as CompletableFuture<Void> and not returning collection of results or something else? I think that was good idea to make CompletableFuture.anyOf return CompletableFuture<Object>, but I see these two methods connected and so I'm confused about what they return.

anyOf has to somehow tell you what was the result of the specific CompletableFuture whose completion triggered anyOf. That's not necessary in case of allOf because you know which futures completed -- all of them.
allOf (just as anyOf) doesn't require that all futures bear the same type. So if it were to return a future of collection, it would have to be a collection of Object which is probably not what you want anyway.
If you really want to have allOf return a future of collection, it's fairly straightforward to write your own:
public static CompletableFuture<List<Object>> myAllOf(CompletableFuture<?>... futures) {
return CompletableFuture.allOf(futures)
.thenApply(x -> Arrays.stream(futures)
.map(f -> (Object) f.join())
.collect(toList())
);
}
If you have a type-safe version of this problem and need to convert a collection of futures of a certain type to a future of collection of that same type, see this question for several examples: List<Future> to Future<List> sequence

This is similar to Misha's answer, but with a generic type:
public class Futures {
public static <T> CompletableFuture<List<T>> all(List<CompletableFuture<T>> futures) {
CompletableFuture<Void> cfv = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
return cfv.thenApply(future -> {
return futures.stream()
.map(completableFuture -> completableFuture.join())
.collect(Collectors.toList());
});
}
}
That's the method the Java API should provide.

Related

Verify size of list of completablefutures

I have a code which looks similar to
public void myMethod(){
List<CompletableFuture<Void>> futures = new ArrayList<>();
//getQuestion, getAnswer, both returns a CompletableFuture<Void>
futures.add(getQuestion());
futures.add(getAnswer());
// make sure all the futures are executed.
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
}
Any pointers on how can I like test if the futures array was of size 2 after the execution of myMethod()*
There is no point in testing the size of the array. In Java, arrays never change their size, which also applies to the array returned by futures.toArray(…). If the size of the array was wrong, it implied that the toArray implementation of the particular collection already returned an array of the wrong size.
You can make the code more robust by using futures.toArray(new CompletableFuture[0])), letting the toArray implementation create an array of the right size instead of passing in a presized array. As explained in this article, it’s actually even faster, contrary to the old myths.
Of course, for a fixed number of futures, there’s no need to translate between collection and array at all. You could use
public void myMethod() {
CompletableFuture<?>[] futures = { getQuestion(), getAnswer() };
// make sure all the futures are executed.
CompletableFuture.allOf(futures).join();
}
The contract of CompletableFuture.allOf is to return a future that will be completed when all futures are completed and join() will unconditionally wait for that completion. There is nothing that would need to be checked. If any of the future completed exceptionally, the join() method will throw an exception, either a CompletionException having the original exception as a cause or a CancellationException when the exceptional completion was caused by cancellation.
You could split the method in two:
public void myMethod(){
List<CompletableFuture<Void>> futures = constructFutures();
// make sure all the futures are executed.
CompletableFuture
.allOf(futures.toArray(new CompletableFuture[futures.size()]))
.join();
}
List<CompletableFuture<Void>> constructFutures() {
List<CompletableFuture<Void>> futures = new ArrayList<>();
//getQuestion, getAnswer, both returns a CompletableFuture<Void>
futures.add(getQuestion());
futures.add(getAnswer());
return futures;
}
And add the test for constructFutures().

Java CompletableFutures: is it ever a acceptable to not join/get?

Is it ever correct to not return or join on a CompletableFuture?
That is, is this code ever correct?
void noJoin() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
}
In the above code, we never know if the Futures complete successfully, or at all. (And I don't even know that the Futures created in noJoin are not eligible for garbage collection; but presumably the ForkJoinPool.commonPool() holds onto those references?)
In any case, if it truly doesn't matter to our program whether or not those Futures succeed, a better implementation of noJoin is just a no-op:
void noJoin() {}
If on the other hand, it does matter, we need to either join on or return both Futures, by composing them (either serially with thenCompose, or in parallel with thenCombine or allOf) and then join the composed Future or return it to our caller.
By joining, we block and throw an exception if either Future is exceptional; or better, by returning a Future, we remain asynchronous while ensuring that our caller gets a Future that holds any exceptional result:
Future<Void> returnBothAsync() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
return CompletableFuture.allOf(cfa, cfb);
}
or
void joinBothBlocking() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
CompletableFuture.allOf(cfa, cfb).get(50L, TimeUnit.MILLISECONDS);
}
I think this is true even if we arrange to handle all exceptions:
void noJoin() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect)
.exceptionally(e -> {
Logger.log(e);
return DEFAULT;
});
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
}
because even if the exceptional are handled / "can't happen", we still don't know if the Future ever completed at all.
Or am I wrong, and there are cases where code like that in noJoin is correct?
This is not a complete answer to your question. It probably depends on the exact use case to be able to tell how to handle CompletableFuture and their results.
If you choose to not wait for the results of the CompletableFutures, you will likely have to make sure that the used executor finishes all tasks. In your case the used executor is ForkJoinPool.commonPool() whose documentation says:
[...] However this pool and any ongoing processing are automatically terminated upon program System.exit(int). Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.

Combining results of two CompletableFuture with different return types

I have 2 different CompletableFuture that I like to run in parallel (each has a different return type) and then to combine their results:
CompletableFuture<Person> person = personDB.asyncCall(..);
CompletableFuture<Dog> dog = dogDB.asyncCall(...);
Now I like to combine the dog.name & the person name:
return dog.getName() + person.getName()
I was trying to use
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(perons, dog);
combinedFuture.thenApply(aVoid -> {
// now what?
});
But got suck here.
Use get() method to wait for them to finish.
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(perons, dog);
combinedFuture.get(); // wait for all of them to complete
return dog.getName() + person.getName();
See https://www.baeldung.com/java-completablefuture
Notice that the return type of the CompletableFuture.allOf() is a
CompletableFuture. The limitation of this method is that it does
not return the combined results of all Futures. Instead you have to
manually get results from Futures. Fortunately,
CompletableFuture.join() method and Java 8 Streams API makes it
simple:

CompletableFuture get result of first future waiting for other futures depending of first one

I have following requirements.
CreateDocument
For document create many release notes (releaseNotesFuture)
For document create many parcels (parcelsFuture)
return objectId of document created in 1.
this is my current code:
public CompletableFuture<ObjectId> createDeliveryNoteDocument(String productId, List<String> releaseNotesIds) {
CompletableFuture<ObjectId> deliveryNoteFuture =
CompletableFuture
.supplyAsync(() -> sequenceServiceFeignClient.getNextValueForSequenceNameNoResponseEntity(DocumentType.DELIVERYNOTE.toString()))
.whenComplete((result, error) -> {
if (error != null)
logger.error("Unable to get next sequence number for DELIVERYNOTE sequence", error);
})
.thenCompose(seqNumber -> {
Set<ObjectAttribute> objectAttributes = new HashSet<>();
objectAttributes.add(new ObjectAttribute(Constants.Document.DOCUMENT_TYPE, DocumentType.DELIVERYNOTE.toString()));
objectAttributes.add(new ObjectAttribute(Constants.Document.DOCUMENT_NO, seqNumber));
objectAttributes.add(new ObjectAttribute(Constants.Document.PRODUCT_ID, productId));
return objectCommandService.createCustomObject(new ObjectTypeTableName(Constants.ObjectTables.DOCUMENT), objectAttributes);
});
CompletableFuture<Void> releaseNotesFuture =
deliveryNoteFuture
.thenComposeAsync(deliveryNoteId -> joinReleaseNotesWithDeliveryNote(deliveryNoteId, releaseNotesIds));
CompletableFuture<Void> parcelsFuture =
deliveryNoteFuture
.thenComposeAsync(deliveryNoteId -> changeParcelsStatusForReleaseNotes(releaseNotesIds));
return deliveryNoteFuture;
}
how could I wait for releaseNotesFuture and parcelsFuturecompletion and then return deliveryNoteFuture result or error if any of releaseNotesFuture or parcelsFuture finished exceptionally?
Instead of returning deliveryNoteFuture, you'll have to have a CompletableFuture that is completed when the releaseNotesFuture and parcelsFuture are completed. Furthermore, you'll want that future to then compose into the result of deliveryNoteFuture, since you want its ObjectId if the whole chain is successful.
Something like
return CompletableFuture.allOf(releaseNotesFuture, parcelsFuture).thenComposeAsync(r -> deliveryNoteFuture);
Since both releaseNotesFuture and parcelsFuture are dependent on deliveryNoteFuture, errors from deliveryNoteFuture will propagate through all these chains.
Similarly, if either of releaseNotesFuture or parcelsFuture fail, the CompletableFuture returned by allOf will be completed with that failure's cause and that cause will be propagated to the future returned by thenComposeAsync. Otherwise, the result of deliveryNoteFuture will be delivered.
As Holger suggests, since you only have those two futures, you could also use thenCombine
releaseNotesFuture.thenCombineAsync(parcelsFuture, (x, y) -> deliveryNoteFuture.join());
Here, the join() won't block since deliveryNoteFuture is definitely already completed. Again, if the deliveryNoteFuture had originally failed, its cause will be propagated down the chain ending with the CompletableFuture returned by thenCombineAsync.

How to convert the code to use CompletableFuture?

I used to have a callable class
class SampleTask implements Callable<Double> {
#Override
public Double call() throws Exception {
return 0d;
}
}
I used to use ExecutorService to submit the Callable. How to change to use CompletableFuture.supplyAsync?
The following code cannot compile
SampleTask task = new SampleTask();
CompletableFuture.supplyAsync(task);
No instance of type of variable U exists so that SampleTask conforms to Supplier
For your callable as written, you could simply use CompletableFuture.supplyAsync(() -> 0d);.
If, however, you have an existing Callable, using it with CompletableFuture is not so straight-forward due to the checked exceptions that a callable might throw.
You may use an ad-hoc Supplier which catches exceptions and re-throws it wrapped in an unchecked exception like
CompletableFuture.supplyAsync(() -> {
try { return callable.call(); }
catch(Exception e) { throw new CompletionException(e); }
})
Using the specific type CompletionException instead of an arbitrary subtype of RuntimeException avoids getting a CompletionException wrapping a runtime exception wrapping the actual exception when calling join().
Still, you’ll notice the wrapping when chaining an exception handler to the CompletableFuture. Also, the CompletionException thrown by join() will be the one created in the catch clause, hence contain the stack trace of some background thread rather than the thread calling join(). In other words, the behavior still differs from a Supplier that throws an exception.
Using the slightly more complicated
public static <R> CompletableFuture<R> callAsync(Callable<R> callable) {
CompletableFuture<R> cf = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
try { cf.complete(callable.call()); }
catch(Throwable ex) { cf.completeExceptionally(ex); }
});
return cf;
}
you get a CompletableFuture which behaves exactly like supplyAsync, without additional wrapper exception types, i.e. if you use
callAsync(task).exceptionally(t -> {
t.printStackTrace();
return 42.0;
})
t will be the exact exception thrown by the Callable, if any, even if it is a checked exception. Also callAsync(task).join() would produce a CompletionException with a stack trace of the caller of join() directly wrapping the exception thrown by the Callable in the exceptional case, exactly like with the Supplier or like with runAsync.
supplyAsync() expects a Supplier<U> and you are giving it a Callable.
The error message is telling you that the compiler has tried to find a type to use for U such that your SampleTask "is a" Supplier<U>, but it can't find one.
Java will implicitly "promote" a lambda to a functional interface such as Callable or Supplier. But it won't treat functional interfaces as interchangeable -- that is, you can't use a Callable where a Supplier is expected.
You can make a suitable lambda in-place:
SimpleTask task = new SimpleTask();
CompletableFuture.supplyAsync(() -> task.call());
Note that this works if SimpleTask's call() is:
public Double call() { // note no exception declared
return 0d;
}
The fact that SimpleTask happens to implement Callable is not relevant to the code above.
If you want this to work with an arbitrary Callable, or if you declare task as a Callable:
Callable callable = new SimpleTask();
CompletableFuture.supplyAsync(() -> callable.call());
... then you will get a compiler error about the uncaught exception. Your lambda will need to catch the exception and handle it (perhaps rethrowing as an unchecked exception, as described in other answers).
Or you could make SampleTask implement Supplier<Double>.
Part of the motivation for lambdas is that writing things like Callable was too verbose. So you might well leave out the intermediate class and go directly for:
CompleteableFuture<Double> future = CompletableFuture.supplyAsync(() -> 0d);
This applies for more complicated suppliers too:
CompleteableFuture<Double> future = CompletableFuture.supplyAsync(() -> {
Foo foo = slowQuery();
return transformToDouble(foo);
});
Since CompleteableFuture::supplyAsync expects a Supplier<Double> and not Callable<Double> you should go with:
Callable<Double> task = new SampleTask();
CompletableFuture.supplyAsync(() -> {
try {
return task.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
});
I had this come up recently and used Vavr to solve it (was already using it for other things, too), and it worked out great for me:
CompletableFuture.supplyAsync( () -> Try.ofCallable( callable ).get() )
Or to get a Supplier of that CompletableFuture:
() -> CompletableFuture.supplyAsync( () -> Try.ofCallable( callable ).get() )
In all cases I tested this returned exactly and threw exactly what the callable itself did.

Categories

Resources