Reactive Programming in Java: How, Why, and Is It Worth Doing? CompletableFuture Brings us to the Async World

Reactive Programming in Java: How, Why, and Is It Worth Doing? CompletableFuture Brings us to the Async World

CompletableFuture Brings us to the Async World

 

In Java 8, you had CompletableFuture. It is based on the Fork/Join framework. Just like parallelizing threads. Actually, the Fork/Join framework appeared in Java 7, but it was difficult to use it there. In Java 8, CompletableFuture became a step forward to the async world.

  

Let’s look at a simple example.

 

In the code, CompletableFuture methods from a standard JDK are marked orange.

 CompletableFuture.png

Suppose we have an API that allows us to:

 

  • Read data (readData) from the source and return CompletableFuture, as it is asynchronous;
  • Process data, for which there are two processors: processData1 and processData2
  • Merge data (mergeData) after they are processed
  • Write data (writeData) to the receiver (Destination)

 

This is a typical task – read data, process it in two threads, then merge the processing results and write somewhere.

 

CompletableFuture Brings us to the Async World.png

 

We have read the data:

 

CompletableFuture data = readData(source);

Then say: once data is read, it should be sent to processing:

CompletableFuture processData1 = data.thenApplyAsync(this::processData1);

It means you should start their processing in a separate thread. As we use an Async postfix here, the processing starts in two different threads:

CompletableFuture processData2 = data.thenApplyAsync(this::processData2);

The functions this::processData1 and this::processData2 will be started in two different threads and executed in parallel. Yet after the parallel execution, their results should be merged. This is done by thenCombine.

Here we started two execution threads, and once they were completed, we combined them. thenCombine works like this: it waits for processData1 and processData2 to complete, and then calls the function to combine data:

.thenCombine(processData2,

(a, b)->mergeData(a,b))

So we combine the results of the first and second processing and then write data:

.thenAccept(

 

data->writeData(data, dest));

 

Here we get a chain that is actually a business process. It’s as if we are saying: “Kathy, take data from the archive and give it to Helen and George for processing. When Helen and George bring the results, pass them to Veronica to combine them and then pass it to Victor to write a report based on that data.”

 

We have no definite schedule here, about which we spoke earlier: there is a possibility to pass data as soon as we can. The only thing that is waiting is thenCombine. It waits for both processes (the results of which it combines) to complete.

 

CompletableFuture is a cool approach indeed, which helps us build asynchronous systems.

 

In a future article we will be looking at high-level approach to asynchronous operations and review reactive programming operators.

 

Interested in learning how to program with Java or in upgrading your Java programming skills?

Check out our trainings