public static <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futures) {
        return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new))
                .thenApply(__ -> {
                    List<T> results = new ArrayList<>();
                    for (CompletableFuture<T> f : futures) {
                        try {
                            results.add(f.get());
                        } catch (InterruptedException | ExecutionException e) {
                            // Should not happen since all futures are completed without exception
                            throw new IllegalStateException(e);
                        }
                    }
                    return results;
                });
    }
Similar to {@link CompletableFuture#allOf(CompletableFuture[])} but returns a list of the results
vespajlib/src/main/java/com/yahoo/concurrent/CompletableFutures.java