CompletableFuture vs Future: going async with Java 8 new features
Java 8 was released on March 2014 and arrived with a long list of new features. One of the less talked about, extremely useful yet misunderstood features is a brand new and improved extension to the Future interface: CompletableFuture<T>.
In the following post we’ll present an overall view of CompletableFuture, exactly how is it different from a simple Future and when it can be useful.
Asynchronous Java Code
Java 7 introduced us to Future, which represents the result of an asynchronous computation. The main advantage of using the Future object is that you can do other things while waiting for external resources. A non-blocking way to wait for a resource.
Using a Future means you can write a method and instead of it immediately returning the result, it will return a Future object. When you’ll need the actual result, just use Future.get() which will return the value after the computation is done.
You also get methods for checking if the computation is done, and a way to cancel / check if it was canceled.
For example, let’s say you’re making a call to some external resource, like… Marvel’s developer API, pulling out all superheros that have the letter… “C” in their name:
Yup, if we do want go for a fully async non-blocking option we’re out of luck. We have no assurance that the Future is actually there and we might have to wait. This is where CompletableFuture comes in, and helps with a cheeky workaround.
So What’s New in CompletableFuture?
CompletableFuture<T> extends Future<T> and makes it… completable. This is a big deal, considering that Future objects were limited before Java 8, with only 5 methods available.
This new and improved CompletableFuture has 2 main benefits:
- It can be explicitly completed by calling the complete() method without any synchronous wait. It allows values of any type to be available in the future with default return values, even if the computation didn’t complete, using default / intermediate results.
- With tens of new methods, it also allows you to build a pipeline data process in a series of actions. You can find a number of patterns for CompletableFutures such as creating a CompletableFuture from a task, or building a CompletableFuture chain. The full list is available via Oracle’s CompletableFuture documentation.
Back to our simple example, let’s say that Marvel’s API didn’t return a timely result, and getMarvelHeroWithCharacter() is still processing, taking it’s time, while we’re already done with everything else that we wanted to do in the meantime. Assuming we don’t want to wait (for our lives to be over), a CompletableFuture can help us return an intermediate result. Like… Mystique, since at the worst case she can shapeshift into any other superhero.
You can also create a completed CompletableFuture in advance that returns a known value. That might come in handy in your testing environment, in case you’ll want to combine that known value with one that needs to be computed:
Tens of other more useful methods are available and they include transforming and acting on one CompletableFuture (thenApply), running code on completion (thenAccept/thenRun), combining two CompletableFuture together and more. For a complete guide we recommend you to read Java 8: Definitive guide to CompletableFuture.
Meet the Alternatives
If you’re using Guava or Scala (with its Futures), this new feature might sound familiar. It’s similar to Guava’s ListenableFuture, which defines a consistent API for Future objects to register completion callbacks.
Similarly to the new CompletableFuture, the ability to add a callback allows responding to incoming events in an asynchronously and effective way. You can register callbacks to be executed when a computation is complete, and support many operations that the basic Future interface can’t support.
When the Going Gets Tough
Using CompletableFuture gives us the ability to run along with our code without having to wait for the results, but it still can’t promise your code won’t break while running in production. When errors occur, you’ll need to to identify and analyze them as quickly as you can to deploy a hotfix.
For these kind of situations, OverOps will enable you to fix issues in your code effectively when they arise, without having to “wait” until someone else run into them.
CompletableFuture fits right in as part of the asynchronous programming trend, that became popular during the past few years. It’s no wonder everyone’s talking about it, since we can use it to run a number of tasks at the same time, which allow an optimal workflow.
In case you’re already a fan of asynchronous programming, you might want to check out our post about 7 Reactive Programming Tools You MUST Know.