简体   繁体   中英

How to aggregate results from making CompletableFuture calls in a loop?

I am just learning and trying to apply CompletableFuture to my problem statement. I have a list of items I am iterating over.

Prop is a class with only two attributes prop1 and prop2, respective getters and setters.

List<Prop> result = new ArrayList<>(); 

for ( Item item : items ) {
      item.load();

      Prop temp = new Prop();
      // once the item is loaded, get its properties
      temp.setProp1(item.getProp1());
      temp.setProp2(item.getProp2());

      result.add(temp);
}

return result;

However, item.load() here is a blocking call. So, I was thinking to use CompletableFuture something like below -

for (Item item : items) {
    CompletableFuture<Prop> prop = CompletableFuture.supplyAsync(() -> {
        try {
            item.load();
            return item;
        } catch (Exception e) {
            logger.error("Error");
            return null;
        }
    }).thenApply(item1 -> {
        try {
            Prop temp = new Prop();
            // once the item is loaded, get its properties
            temp.setProp1(item.getProp1());
            temp.setProp2(item.getProp2());

            return temp;
        } catch (Exception e) {
        }
    });
}

But I am not sure how I can wait for all the items to be loaded and then aggregate and return their result.

I may be completely wrong in the way of implementing CompletableFutures since this is my first attempt. Please pardon any mistake. Thanks in advance for any help.

There are two issues with your approach of using CompletableFuture .

First, you say item.load() is a blocking call, so the CompletableFuture 's default executor is not suitable for it, as it tries to achieve a level of parallelism matching the number of CPU cores. You could solve this by passing a different Executor to CompletableFuture 's asynchronous methods, but your load() method doesn't return a value that your subsequent operations rely on. So the use of CompletableFuture complicates the design without a benefit.

You can perform the load() invocations asynchronously and wait for their completion just using an ExecutorService , followed by the loop as-is (without the already performed load() operation, of course):

ExecutorService es = Executors.newCachedThreadPool();
es.invokeAll(items.stream()
    .map(i -> Executors.callable(i::load))
    .collect(Collectors.toList()));
es.shutdown();

List<Prop> result = new ArrayList<>(); 

for(Item item : items) {
      Prop temp = new Prop();
      // once the item is loaded, get its properties
      temp.setProp1(item.getProp1());
      temp.setProp2(item.getProp2());

      result.add(temp);
}

return result;

You can control the level of parallelism through the choice of the executor, eg you could use a Executors.newFixedThreadPool(numberOfThreads) instead of the unbounded thread pool.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM