Search code examples
javacompletable-future

java CompletableFuture completion sequence


From CompletableFuture javadocs:

Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.

and looking into CompletableFuture.complete() it calls postComplete() which seems to pop off the dependent chains and tries to complete them.

To test my understanding, I wrote a simple program

import java.util.concurrent.*;
public class TestCompletableFuture {
    
    public static void main(String[] args) throws Exception {
        CompletableFuture<Void> future = new CompletableFuture<Void>()
            .whenComplete((res, exc) -> {
                System.out.println("inside handle.");
                if (exc != null) {
                    System.out.println("exception.");
                }
                System.out.println("completed.");
            }
        );

        future.completeExceptionally(new Exception("exception"));
        System.out.println("done.");
    }
}

the output of the code:

done.

From my understanding, when the main thread calls future.completeExceptionally() it should invoke the function passed into CompletableFuture.whenComplete().

Why is this not the case?


Solution

  • This is because you are completing the wrong future. You need to get a reference to the first stage and complete that to see the whenComplete in action:

    public class TestCompletableFuture {
    
        public static void main(String[] args) throws Exception {
            // get reference
            CompletableFuture<Void> future = new CompletableFuture<>();
            
            // configure the action that to be run when the future is complete
            CompletableFuture<Void> future2 = future
                    .whenComplete((res, exc) -> {
                                System.out.println("inside handle.");
                                if (exc != null) {
                                    System.out.println("exception.");
                                }
                                System.out.println("completed.");
                            }
                    );
    
            future.completeExceptionally(new Exception("exception"));
            System.out.println("done.");
        }
    }
    

    So, now the code speaks for itself... When the future is complete run that action (res, exc) -> {...}. And then just trigger that completion on the future by calling completeExceptionally(...) on it.

    Another thing to note is that all of the above stages (futures) are completed exceptionally now:

    System.out.println(future.isDone()); // true
    System.out.println(future2.isDone()); // true
    
    System.out.println(future.isCompletedExceptionally()); // true
    System.out.println(future2.isCompletedExceptionally()); // true