How do we send multiple requests concurrently and wait for all of the responses to be available?
As we know, CompletableFuture comes with the allOf() method (for more details, please read Chapter 11, Concurrency – Deep Dive), which can execute tasks in parallel and waits for all of them to complete. CompletableFuture<Void> is returned.
The following code waits for the responses to four requests:
List<URI> uris = Arrays.asList(
new URI("https://reqres.in/api/users/2"), // one user
new URI("https://reqres.in/api/users?page=2"), // list of users
new URI("https://reqres.in/api/unknown/2"), // list of resources
new URI("https://reqres.in/api/users/23")); // user not found
HttpClient client = HttpClient.newHttpClient();
List<HttpRequest> requests = uris.stream()
.map(HttpRequest::newBuilder)
.map(reqBuilder -> reqBuilder.build())
.collect(Collectors.toList());
CompletableFuture.allOf(requests.stream()
.map(req -> client.sendAsync(
req, HttpResponse.BodyHandlers.ofString())
.thenApply((res) -> res.uri() + " | " + res.body() + " ")
.exceptionally(e -> "Exception: " + e)
.thenAccept(System.out::println))
.toArray(CompletableFuture<?>[]::new))
.join();
To collect the bodies of the responses (for example, in List<String>), consider the WaitAllResponsesFetchBodiesInList class, which is available in the code that's bundled with this book.
Using a custom Executor object can be accomplished as follows:
ExecutorService executor = Executors.newFixedThreadPool(5);
HttpClient client = HttpClient.newBuilder()
.executor(executor)
.build();