CompletetableFuture — Functions

Prateek
4 min readMar 12, 2022

What is CompletableFuture?

  • Introduced in Java 8
  • CompletableFuture is an Asynchronous Reactive Functional Programming API
  • Asynchronous Computations in a functional Style
  • CompletableFutures API is created to solve the limitations of Future API

Responsive:
• Fundamentally Asynchronous
• Call returns immediately and the response will be sent when its available

Resilient:
• Exception or error won’t crash the app or code

Elastic:
• Asynchronous Computations normally run in a pool of
threads
• No of threads can go up or down based on the need

Message Driven:
• Asynchronous computations interact with each through messages in a event-driven style

— — — — — — — — — — — — — — — — — — — — — — — — — —

SupplyAsync vs RunAsync — runAsync takes Runnable as input parameter and returns CompletableFuture<Void>, which means it does not return any result.

CompletableFuture<Void> run = CompletableFuture.runAsync(()-> System.out.println("hello"));

But suppyAsync takes Supplier as argument and returns the CompletableFuture<U> with result value, which means it does not take any input parameters but it returns result as output.

CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello");
return "result";
});
System.out.println(supply.get()); //result

Conclusion : So if you want the result to be returned, then choose supplyAsync or if you just want to run an async action, then choose runAsync.

— — — — — — — — — — — — — — — — — — — — — — — — — -

CompletableFuture API?

  1. Factory Methods: Initiate Asynchronous computation.

2. Completion Stage Methods: Chain Asynchronous computation.

3. Exception Methods: Handle Exception in an Asynchronous computation.

— — — — — — — — — — — — — — — — — — — — — — — — — —

CompletableFuture.supplyAsync — Returns a new CompletableFuture that is asynchronously completed by a task running in the ForkJoinPool.commonPool() with the value obtained by calling the given Supplier.

  • Factory Method
  • Initiate Asynchronous computation
  • Input is Supplier Functional Interface
  • Returns CompletableFuture<T>

— — — — — — — — — — — — — — — — — — — — — — — — — — — —

public <U> CompletableFuture<U> thenApply( java.util.function.Function<? super T, ? extends U> fn )
java.util.concurrent.CompletionStage Returns a new CompletionStage that, when this stage completes normally, is executed with this stage’s result as the argument to the supplied function.
This method is analogous to Optional.map and Stream.map.
See the CompletionStage documentation for rules covering exceptional completion.

  • Completion Stage method
  • Transform the data from one form to another
  • Input is Function Functional Interface
  • Returns CompletableFuture<T>

— — — — — — — — — — — — — — — — — — — — — — — — — — —

public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}

CompletionStage Returns a new CompletionStage that, when this stage completes normally, is executed with this stage’s result as the argument to the supplied action. See the CompletionStage documentation for rules covering exceptional completion.

  • Completion Stage method,
  • Chain Asynchronous computation,
  • Input in consumer Functional Interfaces — consumes the result of previous
  • Returns CompletableFuture<Void>
  • Use it at the end of Asynchronous computation.

— — — — — — — — — — — — — — — — — — — — — — — — —

public static CompletableFuture<Object> anyOf(
@NotNull CompletableFuture<?>... cfs
)

Returns a new CompletableFuture that is completed when any of the given CompletableFutures complete, with the same result. Otherwise, if it completed exceptionally, the returned CompletableFuture also does so, with a CompletionException holding this exception as its cause. If no CompletableFutures are provided, returns an incomplete CompletableFuture.

public String anyOf() {
startTimer();

CompletableFuture<String> db = CompletableFuture.supplyAsync(() -> {
delay(3500);
log("response from db");
return "Hello World";
});

CompletableFuture<String> restApi = CompletableFuture.supplyAsync(() -> {
delay(2000);
log("response from restApi");
return "Hello World";
});

CompletableFuture<String> soapApi = CompletableFuture.supplyAsync(() -> {
delay(3000);
log("response from soapApi");
return "Hello World";
});

List<CompletableFuture<String>> cfList = List.of(db, restApi, soapApi);
CompletableFuture<Object> cfAllOf = CompletableFuture.anyOf(cfList.toArray(new CompletableFuture[cfList.size()]));
String result = (String) cfAllOf.thenApply(v -> {
if (v instanceof String) {
return v;
}
return null;
}).join();

timeTaken();
return result;
}

Test cases:

@Test
void anyOf() {
String result = cfhw.anyOf();
assertEquals("Hello World", result);
}

Console Output —

[ForkJoinPool.commonPool-worker-5] — response from restApi
[Test worker] — Total Time Taken : 2065
BUILD SUCCESSFUL in 5s

— — — — — — — — — — — — — — — — — — — — — — — — — —

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
return andTree(cfs, 0, cfs.length - 1);
}

Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a CompletionException holding this exception as its cause. Otherwise, the results, if any, of the given CompletableFutures are not reflected in the returned CompletableFuture, but may be obtained by inspecting them individually. If no CompletableFutures are provided, returns a CompletableFuture completed with the value null.
Among the applications of this method is to await completion of a set of independent CompletableFutures before continuing a program, as in: CompletableFuture.allOf(c1, c2, c3).join();

public String allOf() {
startTimer();

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
delay(1000);
return "Hello";
});

CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
delay(2000);
return " World";
});

List<CompletableFuture<String>> cfList = List.of(cf1, cf2);
CompletableFuture<Void> cfAllOf = CompletableFuture.allOf(cfList.toArray(new CompletableFuture[cfList.size()])); String result = cfAllOf.thenApply(v -> cfList.stream()
.map(CompletableFuture::join)
.collect(joining())).join();

timeTaken();
return result;
}

Test Case:

@Test
void allOf() {
String result = cfhw.allOf();
assertEquals("Hello World", result);
}

[Test worker] — Total Time Taken : 2027
BUILD SUCCESSFUL in 4s
3 actionable tasks: 3 executed

— — — — — — — — — — — — — — — — — — — — — — — — — — —-

--

--