completable-futures

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();