Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Как выполнить асинхронные задачи и объединить результаты

Короткий ответ

supplyAsync() запускает задачу в фоне, thenApply() описывает, что сделать с результатом, join() ждёт итог:

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

Результат:

Hello, ANNA

Объединить два результата

thenCombine() ждёт две независимые задачи и объединяет их результаты:

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

Результат:

100

Дождаться всех

allOf() завершается, когда готовы все задачи:

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

Результат:

6

Обработка ошибок

exceptionally() подставляет значение, если на каком-то шаге произошла ошибка:

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

Результат:

Failed: For input string: "x"
0

Частая ошибка

Забыть дождаться результата. Программа завершается, а фоновые задачи из общего пула не успевают выполниться — их вывод просто пропадает:

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

Результат:

main finished

Вызывайте join() или get(), если результат нужен до выхода из программы.

Функции из этого ответа

  • CompletableFuture.supplyAsync() — Запускает вычисление асинхронно и возвращает `CompletableFuture`.
  • CompletableFuture.thenApply() — Добавляет шаг, который преобразует результат, когда тот будет готов.
  • CompletableFuture.thenCombine() — Объединяет результаты двух независимых задач, когда готовы обе.
  • CompletableFuture.join() — Ждёт результат `CompletableFuture`; ошибки приходят как непроверяемые исключения.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.