Ke Vin
Ke Vin

Reputation: 3750

How to execute CompletableFuture function and get the result either which of them is done first?

i already have create 3 function that execute query to database, then i want to get each result and add into one List of object but the result is always empty, how can i do this properly? here is what i do :

i create 3 CompletableFuture function :

private CompletableFuture<List<TransSalesOrderOnlyResponseDto>> findPureUnAssignedSO() {
    return CompletableFuture.supplyAsync(() -> iSalesOrderMapper.entityToSOOnlyDto(iTransSalesOrderQdslRepository.findUnAssignedSalesOrder()));
}

private CompletableFuture<List<TransSalesOrderOnlyResponseDto>> findSOHaveItemLeftOverOnly() {
    return CompletableFuture.supplyAsync(() -> {
        List<TransSalesOrder> transSalesOrders = iTransSalesOrderQdslRepository.findSOHaveLeftOverButDone();
        return buildTransSalesOrdersResponseNew(transSalesOrders);
    });
}

private CompletableFuture<List<TransSalesOrderOnlyResponseDto>> findSalesOrderWithBpsjInDeliveryOrder() {
    return CompletableFuture.supplyAsync(() -> {
        List<TransSalesOrder> transSalesOrders = iTransSalesOrderQdslRepository.findSalesOrderWithBpsjInDeliveryOrder();

        return buildTransSalesOrdersBpsjOnlyResponseNew(transSalesOrders); 
    });
}

and then here is how i try to execute that 3 function :

ATTEMPT 1, using get() :

public List<TransSalesOrderOnlyResponseDto> findUnAssignedSO() {
    CompletableFuture<List<TransSalesOrderOnlyResponseDto>> future = new CompletableFuture<>();

    List<TransSalesOrderOnlyResponseDto> transSalesOrdersResponseNew = new ArrayList<>();
    try {
        transSalesOrdersResponseNew = findSOHaveItemLeftOverOnly().get();
        transSalesOrdersResponseNew.addAll(findPureUnAssignedSO().get());
        transSalesOrdersResponseNew.addAll(findSalesOrderWithBpsjInDeliveryOrder().get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }

    return transSalesOrdersResponseNew;
}

the result still empty

ATTEMPT 2 :

public List<TransSalesOrderOnlyResponseDto> findUnAssignedSO() {
    List<TransSalesOrderOnlyResponseDto> transSalesOrdersResponseNew = new ArrayList<>();

    CompletableFuture<List<TransSalesOrderOnlyResponseDto>> soHaveItemLeftOverOnly = findSOHaveItemLeftOverOnly();
    CompletableFuture<List<TransSalesOrderOnlyResponseDto>> pureUnAssignedSO = findPureUnAssignedSO();
    CompletableFuture<List<TransSalesOrderOnlyResponseDto>> salesOrderWithBpsjInDeliveryOrder = findSalesOrderWithBpsjInDeliveryOrder();

    CompletableFuture.allOf(soHaveItemLeftOverOnly, pureUnAssignedSO, salesOrderWithBpsjInDeliveryOrder)
            .thenRun(() -> {
                transSalesOrdersResponseNew.addAll(soHaveItemLeftOverOnly.join());
                transSalesOrdersResponseNew.addAll(pureUnAssignedSO.join());
                transSalesOrdersResponseNew.addAll(salesOrderWithBpsjInDeliveryOrder.join());
                });

    }

    return transSalesOrdersResponseNew;
}

the result is always empty if i do this, even i use .get() to block the result, how do i do completablefuture properly?

Upvotes: 0

Views: 1519

Answers (1)

Davide D&#39;Alto
Davide D&#39;Alto

Reputation: 8206

Both your attempts aren't working because you call an a completion stage without waiting for the result (I'm not sure about attempt number 1 though).

I don't know the signature of all the methods, but if somewhere you are returning a CompletionStage in the supplyAsync and you don't use thenCompose instead, the function will return ignoring the result of the CompletionStage

In Attempt number 2, it's easier to see where it's wrong. It's this part:

CompletableFuture.allOf(...).thenRun(() -> ...);

It doesn't matter what you do in the thenRun part. You don't wait anywhere for the result, so it will get to the return transSalesOrdersResponseNew; immediately, even if the function you have defined in the thenRun part hasn't finished yet.

Assuming that the methods findSOHaveItemLeftOverOnly, findPureUnAssignedSO and findSalesOrderWithBpsjInDeliveryOrder are correct (we cannot know that from the details you gave use), you could rewrite the code this way:

final List<TransSalesOrderOnlyResponseDto> transSalesOrdersResponseNew = ... ;

findSOHaveItemLeftOverOnly()
   .thenAccept( transSalesOrdersResponseNew::addAll )
   .thenCompose( v -> findPureUnAssignedSO() )
   .thenAccept( transSalesOrdersResponseNew::addAll )
   .thenCompose( v -> findSalesOrderWithBpsjInDeliveryOrder() )
   .thenAccept( transSalesOrdersResponseNew::addAll )
   .join();

return transSalesOrdersResponseNew;

Note that I'm using .thenCompose, this will use the result of the function as the next CompletionStage in the sequence. This way you won't lose result in the process.

You could also run the find methods in parallel (if the order doesn't matter) with CompletableFuture.allOf but in that case you need to make sure to use an implementation of List that's thread safe. It would look like this:

final List<TransSalesOrderOnlyResponseDto> transSalesOrdersResponseNew = ... // Thread-safe list implementation;

CompletableFuture.allOf(
    findSOHaveItemLeftOverOnly().thenAccept( transSalesOrdersResponseNew::addAll ),
    findPureUnAssignedSO().thenAccept( transSalesOrdersResponseNew::addAll ),
    findSalesOrderWithBpsjInDeliveryOrder().thenAccept( transSalesOrdersResponseNew::addAll )
).join();

return transSalesOrdersResponseNew;

Upvotes: 2

Related Questions