This is Part 1 of Future vs CompletableFuture. In this blog, we will be comparing Java 5’s Future with Java 8’s CompletableFuture on the basis of two categories i.e. manual completion and attaching a callable method.
What is CompletableFuture?
CompletableFuture is used for asynchronous programming in Java. Asynchronous programming is a means of writing non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its progress, completion, or failure.
CompletableFuture implements two interfaces:
In these slide, you will have deep understanding of Asynchronous programming in Functional Java with some examples along with the comparison with Scala. Explore Here>>
Future vs. CompletableFuture
1. Manual Completion
Talking about manual completition , future provides an isDone() method to check whether the computation is done or not, and a get() method to retrieve the result of the computation when it is done. But if there comes a scenario where you need to complete it manually, Future does not provide any means to do so.
But in Java 8’s completableFuture, CompletableFuture.complete() method helps us to manually complete a Future. Let’s have a look at an example to see it in more detail.
Creating a CompletableFuture simple by having a no-arg constructor.
To fetch the result, you can use get() method.
Since we know that get() method blocks until the future is complete, the above call will block forever since the future is never completed. Therefore, we can use complete() method in order to manually complete the result.
2. Attaching a callable method
While using Future, we do not get notified when it is complete neither does it provides us a callable method which will automatically be called when the result is available but CompletableFuture provides us with a lot of callable methods which can be used as per our use case. But before we go through callable methods let’s have a basic understanding of runAsync() and supplyAsync() for asynchronous computation.
It is used for running some background tasks asynchronously but not returning anything by using a Runnable instance. It takes a Runnable object and returns CompletableFuture. For example:
In the above example, we have used lambda expression for passing the Runnable object.
This method is used when you want to return some value from the background task running asynchronously. It takes a Supplier and returns CompletableFuture. Here, the Supplier is a functional interface and T is the type of value returned from the supplier. For example:
Note: Async methods are used to run the task on a separate thread apart from the main thread whereas the methods without the Async postfix run the execution stage using a calling thread. When using methods with Async postfix, you can specify the executor as the second argument. By default it is ForkJoinPool.commonPool() method that is used.
Now, let’s have a look at each of the callable method:
It takes a Function as an argument. A function is a functional interface that represents a function that takes argument of type T and returns the argument of type R.
We can even apply a sequence of transformations using thenApply() where the result of 1st thenApply() is passed to the 2nd thenApply() and so on. For example:
It takes a Consumer and returns CompletableFuture. It has access to the result of the CompletableFuture on which it is attached. For example:
It also takes a Consumer and returns CompletableFuture. If you neither need the value of the computation nor want to return some value at the end of the chain, then you can pass a Runnable lambda to the thenRun() method. Hence, in this case, we do not have access to future’s result.
- While processing results of asynchronous computations, we also have callable methods with async variants where we can specify the executor as discussed above.
- The consumer methods are often used as the last callback in the callback chain.
- Combining 2 CompletableFutures together
- Combining multiple CompletableFutures together
- Exception Handling