Reputation: 5537
I am trying to get a grip of Java 8 CompletableFuture. How can I join these to person and return them after "allOf". The code under is not working but gives you an idea of what I have tried.
In javascript ES6 i would do
Promise.all([p1, p2]).then(function(persons) {
console.log(persons[0]); // p1 return value
console.log(persons[1]); // p2 return value
});
My efforts in Java so far
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
@Test
public void combinePersons() throws ExecutionException, InterruptedException {
CompletableFuture<Person> p1 = CompletableFuture.supplyAsync(() -> {
return new Person("p1");
});
CompletableFuture<Person> p2 = CompletableFuture.supplyAsync(() -> {
return new Person("p1");
});
CompletableFuture.allOf(p1, p2).thenAccept(it -> System.out.println(it));
}
Upvotes: 16
Views: 21307
Reputation: 77
CompletableFuture.allOf(p1, p2); // .get can be ignored
List<Person> res =
List.of(p1, p2) // list of futures
.stream()
.map(future -> {
System.out.println("future " + future.join());
return future.join();
})
.collect(Collectors.toList());
Or you can fetch the values individually, using p1.get() and p2.get()
Upvotes: 1
Reputation: 2878
As pointed out in @Sotirios Delimanolis's answer, the CompletableFuture is not exception friendly, which means we cannot get the result of all futures easily when exceptions happen in one or several of the futures.
As OP's question is not limited to the successful situation, in this answer, I want to make an addition to this imperfect of CompletableFuture.
If we however want to know the results of all the futures, we can handle this separately when defining futures. For example:
CompletableFuture<Person> p1 = CompletableFuture.supplyAsync(() -> {
return new Person("p1");
});
p1.thenAccept(person -> {
// handle successful future
// for example: add p1 to success operation list
});
p1.exceptionally((exception) -> {
// handle fail future
// for example, log the parameter (not exist in this case) to create the person
System.out.println("Exception happen in creating p1");
return null;
});
and then after calling CompletableFuture.allOf(persons).join()
(Please also pay attention to the exception handling here), we can figure out which future succeeds and which future fails.
Hope this simple tip can help newcomers write solid code in real-world business systems.
Upvotes: 1
Reputation: 279990
The CompletableFuture#allOf
method does not expose the collection of completed CompletableFuture
instances that were passed to it.
Returns a new
CompletableFuture
that is completed when all of the givenCompletableFuture
s complete. If any of the givenCompletableFuture
s complete exceptionally, then the returnedCompletableFuture
also does so, with aCompletionException
holding this exception as its cause. Otherwise, the results, if any, of the givenCompletableFuture
s are not reflected in the returnedCompletableFuture
, but may be obtained by inspecting them individually. If noCompletableFuture
s are provided, returns aCompletableFuture
completed with the valuenull
.
Note that allOf
also considers futures that were completed exceptionally as completed. So you won't always have a Person
to work with. You might actually have an exception/throwable.
If you know the amount of CompletableFuture
s you're working with, use them directly
CompletableFuture.allOf(p1, p2).thenAccept(it -> {
Person person1 = p1.join();
Person person2 = p2.join();
});
If you don't know how many you have (you're working with an array or list), just capture the array you pass to allOf
// make sure not to change the contents of this array
CompletableFuture<Person>[] persons = new CompletableFuture[] { p1, p2 };
CompletableFuture.allOf(persons).thenAccept(ignore -> {
for (int i = 0; i < persons.length; i++ ) {
Person current = persons[i].join();
}
});
If you wanted your combinePersons
method (ignoring it's a @Test
for now) to return a Person[]
containing all the Person
objects from the completed futures, you could do
@Test
public Person[] combinePersons() throws Exception {
CompletableFuture<Person> p1 = CompletableFuture.supplyAsync(() -> {
return new Person("p1");
});
CompletableFuture<Person> p2 = CompletableFuture.supplyAsync(() -> {
return new Person("p1");
});
// make sure not to change the contents of this array
CompletableFuture<Person>[] persons = new CompletableFuture[] { p1, p2 };
// this will throw an exception if any of the futures complete exceptionally
CompletableFuture.allOf(persons).join();
return Arrays.stream(persons).map(CompletableFuture::join).toArray(Person[]::new);
}
Upvotes: 27