Completable Futures
Motivation
const breakfast = await Promise.all([ // concurrency
findFood(),
brewCoffee().then(pourCoffee) // chaining
]);
Future<String> foodFuture = executor.submit(() -> findFood());
Future<String> coffeeFuture =
executor.submit(() -> { // chaining manually
var coffee = brewCoffee();
return pourCoffee(coffee);
});
// concurrency
var coffee = coffeeFuture.get();
var food = foodFuture.get();
🤔
CompletableFuture
public class CompletableFuture<T>
implements Future<T>, CompletionStage<T>
Future
Future<Integer> future = CompletableFuture.supplyAsync(() -> {
IO.println("Computing...");
sleep(1000);
return 42;
});
IO.println("Result: " + future.get());
IO.println("Done!");
Chaining
var future = ...
future
.thenApply(result -> "Result: " + result) // Function -> .map()
.thenAccept(IO::println) // Consumer -> .forEach()
.thenRun(() -> IO.println("Done")); // Runnable
IO.println("When is this gonna get printed?");
Combining
var part1 = CompletableFuture.supplyAsync(() -> 42);
var part2 = CompletableFuture.supplyAsync(() -> "Hello World");
CompletableFuture<Thing> combination =
part1.thenCombine(part2, (a, b) -> new Thing(a, b));
combination.thenAccept(IO::println);
GetNow
var future = ...
IO.println("Fake Result: " + future.getNow(23));
IO.println("Done!");
future.thenAccept(r -> IO.println("Result: " + r));
Exceptions
var future = CompletableFuture.supplyAsync(() -> {
if (Math.random() < 0.5)
return "Hello World!";
else
throw new RuntimeException();
});
future.exceptionally(t -> "Error, bad luck")
.thenAccept(System.out::println);
apply vs applyAsync
public interface CompletionStage<T> {
thenApply( ... );
thenApplyAsync( ... );
thenAccept( ... );
thenAcceptAsync( ... );
...
future.thenApply( ... )- Jener Thread, der future ausgeführt hat, führt aus
future.thenApplyAsync( ... )- Irgendein Thread führt aus
Breakfast
CompletableFuture<Food> foodFuture = CompletableFuture
.supplyAsync(Main::findFood);
CompletableFuture<Coffee> coffeeFuture = CompletableFuture
.supplyAsync(Main::brewCoffee)
.thenApply(Main::pourCoffee);
CompletableFuture<Breakfast> breakfastFuture = coffeeFuture
.thenCombine(
foodFuture,
Breakfast::new
);
var breakfast = breakfastFuture.join();