CompletedFuture: how to apply a function to a set of CompletingFutures?

Suppose I have 3 downloads framed as terminating futures:

    CompletableFuture<Doc> dl1 = CompletableFuture.supplyAsync(() -> download("file1"));
    CompletableFuture<Doc> dl2 = CompletableFuture.supplyAsync(() -> download("file2"));
    CompletableFuture<Doc> dl3 = CompletableFuture.supplyAsync(() -> download("file3"));

      

Then they should all be handled the same way.

    CompletableFuture<String> s1 = dl1.thenApply(Doc::getFilename);
    CompletableFuture<String> s2 = dl2.thenApply(Doc::getFilename);
    CompletableFuture<String> s3 = dl3.thenApply(Doc::getFilename);

      

And you can imagine several functions to be applied, all in parallel.

According to the DRY principle, this example seems out of place. So I am looking for a solution to define only 1 workflow that runs 3 times, in parallel.

How can I do that?

I tried allOf

it but it has two problems: 1) it starts blocking and 2) the return type can only handle run

, not handle it.

+3


source to share


1 answer


Stream.of("file1", "file2", "file3") // or your input in any other format, that can easily be transformed to a stream...
      // .parallel() // well... depends...
      .map(s -> CompletableFuture.supplyAsync(() -> download(s)))
      .map(dl -> dl.thenApply(Doc::getFilename))
      .map(CompletableFuture::join) // if you want to have all the results collected
      .collect(Collectors.toList());

      

Of course, you can also combine two characters map

. But at least you don't write just x times ... If you don't like the collection List

, you can also name something else on it, for example. .forEach(System.out::println)

... .forEach

has the advantage that as soon as a response is available, the consumer is called.



Or classic: just use a loop and a list / array for input, but you might have to take care of more than you would with streams

+5


source







All Articles