Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to run async tasks and combine their results

Short answer

supplyAsync() runs a task in the background, thenApply() says what to do with the result, and join() waits for the outcome:

void main() {
    CompletableFuture<String> greeting = CompletableFuture
            .supplyAsync(() -> "anna")
            .thenApply(String::toUpperCase)
            .thenApply(name -> "Hello, " + name);
 
    IO.println(greeting.join());
}

Output:

Hello, ANNA

Combining two results

thenCombine() waits for two independent tasks and combines their results:

int load(String what, int value) {
    try {
        Thread.sleep(200);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return value;
}
 
void main() {
    var price = CompletableFuture.supplyAsync(() -> load("price", 120));
    var discount = CompletableFuture.supplyAsync(() -> load("discount", 20));
 
    IO.println(price.thenCombine(discount, (p, d) -> p - d).join());
}

Output:

100

Waiting for all

allOf() completes when all tasks are done:

void main() {
    var futures = List.of(
            CompletableFuture.supplyAsync(() -> 1),
            CompletableFuture.supplyAsync(() -> 2),
            CompletableFuture.supplyAsync(() -> 3));
 
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    IO.println(futures.stream().mapToInt(CompletableFuture::join).sum());
}

Output:

6

Handling errors

exceptionally() supplies a value if an error happened at some step:

void main() {
    int value = CompletableFuture.supplyAsync(() -> Integer.parseInt("x"))
            .exceptionally(error -> {
                IO.println("Failed: " + error.getCause().getMessage());
                return 0;
            })
            .join();
    IO.println(value);
}

Output:

Failed: For input string: "x"
0

Common mistake

Forgetting to wait for the result. The program ends, and background tasks from the common pool do not get a chance to finish — their output simply disappears:

void main() {
    CompletableFuture.runAsync(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        IO.println("never printed");
    });
    IO.println("main finished");
}

Output:

main finished

Call join() or get() if you need the result before the program exits.

Functions used in this recipe

Thanks for reading!

Donate to the site — we work hard for you.