Converting old Java Future to CompletableFuture

Many Java async libraries still do not support the CompletableFuture, so, sometimes you will need to handle the old Java Future.
The problem is: it has no callback methods. You definatelly need to block to get a result and it really (REALLY) sucks.
This post is basically to show an easy way to convert the old Future into the new CompletableFuture, without any libraries, with only plain Java.

We know that the Java Future interface does not have any callback method, so, the easier way to check whenever the future is completed is to create an event loop that checks both isDone() and isCancelled() methods, then complete it with a promise (which can be a CompletableFuture, in this case), then stop the event loop after it has been completed.

Since we want to spare CPU processing, let's isolate this event loop to use only one thread. Java offers an easy way to create an event loop using the ScheduledExecutorService:

public class CompletablePromiseContext {
    private static final ScheduledExecutorService SERVICE = Executors.newSingleThreadScheduledExecutor();
    
    public static void schedule(Runnable r) {
        SERVICE.schedule(r, 1, TimeUnit.MILLISECONDS);
    }
}

Now we need to create our class that will run the event loop for each future:

public class CompletablePromise<V> extends CompletableFuture<V> {
    private Future<V> future;

    public CompletablePromise(Future<V> future) {
        this.future = future;
        CompletablePromiseContext.schedule(this::tryToComplete);
    }

    private void tryToComplete() {
        if (future.isDone()) {
            try {
                complete(future.get());
            } catch (InterruptedException e) {
                completeExceptionally(e);
            } catch (ExecutionException e) {
                completeExceptionally(e.getCause());
            }
            return;
        }

        if (future.isCancelled()) {
            cancel(true);
            return;
        }

        CompletablePromiseContext.schedule(this::tryToComplete);
    }
}

So, basically, we need to schedule the tryToComplete when you create a CompletablePromise, which is a CompletableFeature. The tryToComplete method try to find out if the future is completed or cancelled in any way. If an exception occur, the completeExceptionally should be invoked. If it is not completed, it should scheduled it again so the event loop continues.

Now, if you have an old Java Future and want it to be a CompletableFuture, you just have to:

public class Main {
    public static void main(String[] args) {
        final ExecutorService service = Executors.newSingleThreadExecutor();
        final Future<String> stringFuture = service.submit(() -> "success");
        final CompletableFuture<String> completableFuture = new CompletablePromise<>(stringFuture);

        completableFuture.whenComplete((result, failure) -> {
            System.out.println(result);
        });
    }
}

I hope this post could be helpful to you.

Any questions, please, leave a comment. ;)

[]'s

Gabriel Francisco

Software Engineer at GFG, 25 years, under graduated in Computer Science and graduated in Service-oriented Software Engineering. Like playing guitar once in a while. Oh, and I'm kind of boring.

São Paulo

comments powered by Disqus