ppb
ppb

Reputation: 2623

Completable Future inside completable future

I have couple of completable future in my code. Inside one of completable future I want another completable future (completable future inside completable future) e.g.

public CompletableFuture<List<Employee>> buildEmployee (List<EmployeeResponse> employeeInfo) {
    return supplyAsync(() -> {

        Map<String, List<EmployeeReward>> rewards = rewards(employeeInfo);
        Map<String, List<EmployeePoints>> points = points(employeeInfo);
    }, executor);
}

In above method rewards and points are two independent sequential call, I want it to parallel call for them for that I tried -

public CompletableFuture<List<Employee>> buildEmployee (List<EmployeeResponse> employeeInfo) {
    return supplyAsync(() -> {

        CompletableFuture<Map<String, List<EmployeeReward>>> rewards = reward(employeeInfo);
        CompletableFuture<Map<String, List<EmployeePoints>>> points = points(employeeInfo);

        CompletableFuture<Void> futures = allOf(rewards, points);
    }, executor);
}
  1. Is is correct way to do this? How can I improve it if not correct way?

I am building <List<Employee>> as below

employeeInfo.stream.map(employee -> Employee.builder().
  .<someEmplyInfo>
  .points(points.getOrDefault(employee.getEmpId, newArrayList()))
);

Upvotes: 1

Views: 2595

Answers (2)

Deepak
Deepak

Reputation: 1397

It is important to handle any exceptions in exceptionally block for individual futures.

Ideally, the flow of control should not be dependent on the exception handling logic, it should be wrapped in a status object that can be used to evaluate if further processing should happen. Adding a thenApply post the allOf method and then fetching the results within the thenApply block should do the trick

public CompletableFuture<List<Employee>> buildEmployee(List<EmployeeResponse> employeeInfo) {
    //Build the future instance for reward
    CompletableFuture<Map<String, List<EmployeeReward>>> rewardsFuture = reward(employeeInfo)
        .exceptionally(throwable -> {
            //Handle the error
            return null;
        });

    //Build the future instance for points
    CompletableFuture<Map<String, List<EmployeePoints>>> pointsFuture = points(employeeInfo)
        .exceptionally(throwable -> {
            //Handle the error for rewards
            return null;
        });

    return CompletableFuture.allOf(rewardsFuture, pointsFuture).thenApply(v -> {
        try {
            Map<String, List<EmployeeReward>> rewardsResult = rewardsFuture.get();
            Map<String, List<EmployeePoints>> pointsResult = pointsFuture.get();

            //Convert the above map to the desired list of string
            List<Employee> buildEmployeeResult = null;
            return buildEmployeeResult;
        }
        catch (Exception e) {
            //Handle exception
            return null;
        }
    }, executor);
}

private CompletableFuture<Map<String, List<EmployeePoints>>> points(List<EmployeeResponse> employeeInfo) {
    return supplyAsync(() -> {
        //Logic for points synchronous
    });
}

private CompletableFuture<Map<String, List<EmployeeReward>>> reward(List<EmployeeResponse> employeeInfo) {
    return supplyAsync(() -> {
        //Logic for rewards synchronous
    });
}

Upvotes: 3

Ryuzaki L
Ryuzaki L

Reputation: 40048

In the above approach you are using Async thread to execute buildEmployee method, which means Async thread is responsible to make two API calls rewards and points and then it will combine the result. so in the above approach this method is executing asynchronously but not the API calls.

But you can do it another way by making API calls Asynchronously, do the reward call asynchronously by using supplyAsync and then do the points call using Main thread. Finally block the main thread until async call get finished and then combine the result

public CompletableFuture<List<Employee>> buildEmployee (List<EmployeeResponse> employeeInfo) {

       // First call is Async call        

    CompletableFuture<Map<String, List<EmployeeReward>>> rewards = CompletableFuture.supplyAsync(()->reward(employeeInfo), executor);

      //Second call by main thread

    Map<String, List<EmployeePoints>>> points = points(employeeInfo);

    // main thread is Blocked and get the result of the future.
      rewards.get(); //throws InterruptedException,ExecutionException

   // Now combine the result and return list

      return CompletableFuture.completedFuture(result);

}

Upvotes: 0

Related Questions