CompletableFuture | thenApply vs thenCompose

前端 未结 5 994
离开以前
离开以前 2020-12-12 12:13

I can\'t get my head around the difference between thenApply() and thenCompose().

So, could someone provide a valid use case?

Fro

5条回答
  •  温柔的废话
    2020-12-12 12:57

    thenCompose() is better for chaining CompletableFuture.

    thenApply() is better for transform result of Completable future.

    You can achieve your goal using both techniques, but one is more suitable for one use case then other.

    public CompletableFuture process(Integer i) {
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(
                () -> new HeavyTask(i).execute());
        return completableFuture;
    }
    
    @SneakyThrows
    public CompletableFuture thenApplyVsThenCompose() {
        // each time calling thenApply() function returns CompletionState
        // so you will get nested Futures 
        // you can think about it like map() java optional
        CompletableFuture> cf1 = CompletableFuture.supplyAsync(
                () -> new HeavyTask().execute())
                .thenApply(i -> process(i));
    
        // to get result you will have to get nested get() calls
        Integer resultFromThenApply = cf1.get().get();
    
        // when calling thenCompose() nested Futures are flatten
        // you can think about it like flatMap() java optional
        CompletableFuture cf2;
        cf2 = CompletableFuture.supplyAsync(
                () -> new HeavyTask().execute())
                .thenCompose(this::process);
    
        // you have to just call one get() since thenCompose was flatten
        Integer resultFromThenCompose = cf2.get();
        return null;
    } 
    

    Other problem that can visualize difference between those two

    How would you implement solution when you do not know how many time you have to apply thenApply()/thenCompose() (in case for example recursive methods)?

    public void nested() throws ExecutionException, InterruptedException {
        CompletableFuture completableFutureToCompose = CompletableFuture.completedFuture(1);
        for (int i = 0; i < 10; i++) {
            log.info("Composing");
            completableFutureToCompose = completableFutureToCompose.thenCompose(this::process);
        }
        completableFutureToCompose.get();
    
        // not achievable using then apply
        CompletableFuture completableFutureToApply = CompletableFuture.completedFuture(1);
        for (int i = 0; i < 10; i++) {
            log.info("Applying");
            completableFutureToApply = completableFutureToApply.thenApply(this::process).get();
        }
        completableFutureToCompose.get();
    }
    
    public CompletableFuture process(Integer i) {
        log.info("PROCESSING");
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(
                () -> new HeavyTask(i).execute());
        return completableFuture;
    }
    
    • Using composing you first create receipe how futures are passed one to other and then execute
    • Using apply you execute logic after each apply invocation

提交回复
热议问题