I have to run multiple external call operations and then obtain the result in a form of list.
I decided to use the CompletableFuture
api, and the code I prepared is pretty disgusting:
The example:
public class Main {
public static void main(String[] args) {
String prefix = "collection_";
List<CompletableFuture<User>> usersResult = IntStream.range(1, 10)
.boxed()
.map(num -> prefix.concat("" + num))
.map(name -> CompletableFuture.supplyAsync(
() -> callApi(name)))
.collect(Collectors.toList());
try {
CompletableFuture.allOf(usersResult.toArray(new CompletableFuture[usersResult.size()])).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
List<User> users = usersResult //the result I need
.stream()
.map(userCompletableFuture -> {
try {
return userCompletableFuture.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private static User callApi(String collection) {
return new User(); //potentially time-consuming operation
}
}
I have the following questions:
- Can I somehow avoid duplicating the
try-catch
block in the stream, where I'm mapping CompletableFuture to User? - Can this code be less sequential (how can I avoid waiting for all the futures to finish?)
Is it ok, to do it this way (will all the futures be resolved in the stream?):
public class Main { public static void main(String[] args) { String prefix = "collection_"; List<User> usersResult = IntStream.range(1, 10) .boxed() .map(num -> prefix.concat("" + num)) .map(name -> CompletableFuture.supplyAsync( () -> callApi(name))) .filter(Objects::nonNull) .map(userCompletableFuture -> { try { return userCompletableFuture.get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return null; }) .collect(Collectors.toList()); } private static User callApi(String collection) { return new User(); //potentially time-consuming operation } }