Search code examples
javaasynchronousfuture

CompletableFuture runs out of order


I have the following code in Java (I use Oracle Java 17):

package com.test;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class Solution {
    public void startCompletableFutureExample() {
        CompletableFuture.runAsync(() -> {
                    System.out.println("async operation");
                })
                .completeOnTimeout(
                        timeout(), 10000, TimeUnit.MILLISECONDS);
    }

    private Void timeout() {
        System.out.println("timeout happens");
        return null;
    }
}

when I call the startCompletableFutureExample() method I have the following output:

timeout happens
async operation

while I'd expect to get the only one as timeout has not been even close:

async operation

Why do I have method completeOnTimeout to be run before runAsync? Am I doing something wrong?


Solution

  • Answering my own question - if it is necessary to run asynchronously CompletableFuture or cancel it by timeout expiration, as Slaw suggested it is better to use handle method. I've got the desired behavior with the following code:

    package com.test;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    
    public class Solution {
        public void startCompletableFutureExample() {
            CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return "async operation";
            }).orTimeout(10, TimeUnit.MILLISECONDS);
    
            f.handleAsync((msg, ex) -> {
                if (ex instanceof TimeoutException) {
                    System.out.println("timeout happens");
                    f.cancel(true);
                } else {
                    System.out.println(msg);
                }
                return null;
            });
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    

    Now if I run it, I'll get a timeout reached first and thus the output would be only:

    timeout happens
    

    However if I set Thread.sleep(5) instead of Tread.sleep(50), I will get only:

    async operation
    

    which is an expected value.