Reputation: 2623
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);
}
I am building <List<Employee>>
as below
employeeInfo.stream.map(employee -> Employee.builder().
.<someEmplyInfo>
.points(points.getOrDefault(employee.getEmpId, newArrayList()))
);
Upvotes: 1
Views: 2595
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
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