Search code examples
javacompletable-future

Combine CompletableFuture


I have this code:

CompletableFuture<ApplicationUserDto> byUserName = applicationUserService
                .findByUserName(getCurrentApplicationUserName());

CompletableFuture<List<FocusDto>> focuses = focusService.findMine(byUserName.join().getFacilities());

CompletableFuture.allOf(byUserName, focuses).join();

Iterable<IdmDataFacade> facilities = idmService.lookupFacilities(new HashSet<>(byUserName.join().getFacilities())).toIterable();

So in focusService.findMine
i'm dependent on the result from
applicationUserService.findByUserName(getCurrentApplicationUserName())

But in my frontend tests all data is displayed in the right way and manner.

I´ve read about thenCompose method on CompletableFuture so i tested this code:

CompletableFuture<ApplicationUserDto> byUserName = applicationUserService
                .findByUserName(getCurrentApplicationUserName());

CompletableFuture<List<FocusDto>> focuses = byUserName
                .thenComposeAsync((result) -> focusService.findMine(result.getFacilities()));

CompletableFuture.allOf(byUserName, focuses).join();

Iterable<IdmDataFacade> facilities = idmService.lookupFacilities(new HashSet<>(byUserName.join().getFacilities())).toIterable();

Both code snippets lead to the exact same results. In the log i can see the threads being used. Even these are equal.

So my questions:

  1. Is CompletableFuture so smart to know that it has to wait for byUserName completion to fill in the parameter in focusService.findMine(byUserName.join().getFacilities())

  2. Which piece of code is better to be used?


Solution

    1. Is CompletableFuture so smart to know that it has to wait for byUserName completion to fill in the parameter in focusService.findMine(byUserName.join().getFacilities())

    That's the purpose of the .join: wait until the result is ready and return it. If you write code like this:

    CompletableFuture<ApplicationUserDto> byUserName = applicationUserService
                    .findByUserName(getCurrentApplicationUserName());
    
    CompletableFuture<List<FocusDto>> focuses = focusService.findMine(byUserName.join().getFacilities());
    

    You are telling the application to block the execution and wait for byUserName to complete before calling findMine, so that you have the parameter for .findMine.

    This also means that you don't need to call .allOf after, because you already know that byUserName is completed.

    You could rewrite everything like this:

    Iterable<IdmDataFacade> facilities = applicationUserService
                    .findByUserName(getCurrentApplicationUserName())
                    .thenCompose(byUserNameResult -> focusService
                        .findMine(byUserNameResult.getFacilities())
                        .thenApply(findMineResult -> idmService
                            .lookupFacilities(new HashSet<>(byUserNameResult.getFacilities())).toIterable()
                        )
                    )
                    .join();
    
    1. Which piece of code is better to be used?

    Who knows? It depends what you are trying to achieve. Usually, it doesn't make sense to .join after every CompletionStage if you are using them.