middle
Какие основные методы есть у CompletableFuture?
Методы CompletableFuture можно разделить на несколько категорий:
1. Создание:
supplyAsync(Supplier<U>)— запускает асинхронное вычисление, возвращающее результат.runAsync(Runnable)— запускает асинхронное вычисление без возврата результата.completedFuture(U value)— создаёт уже завершённыйCompletableFutureс заданным значением.
2. Трансформация результата (map-подобные):
thenApply(Function<T, U>)— принимает результат предыдущего этапа, применяет функцию и возвращает новыйCompletableFuture<U>. АналогmapвStream.thenApplyAsync(Function<T, U>)— то же, но выполняется в отдельном потоке.
Пример
CompletableFuture<Integer> lengthFuture = CompletableFuture
.supplyAsync(() -> "Привет")
.thenApply(String::length); // 6
3. Цепочка асинхронных операций (flatMap-подобные):
thenCompose(Function<T, CompletionStage<U>>)— принимает результат и возвращает новыйCompletionStage. АналогflatMap. Используется, когда каждый шаг сам является асинхронным.
Пример
CompletableFuture<String> result = CompletableFuture
.supplyAsync(() -> getUserId())
.thenCompose(userId -> CompletableFuture.supplyAsync(() -> fetchUser(userId)));
4. Потребление результата:
thenAccept(Consumer<T>)— потребляет результат, не возвращая значения.thenRun(Runnable)— выполняет действие после завершения, не имея доступа к результату.
5. Комбинирование двух CompletableFuture:
thenCombine(CompletionStage<U>, BiFunction<T, U, V>)— ожидает завершения обоих и комбинирует результаты.thenAcceptBoth(CompletionStage<U>, BiConsumer<T, U>)— потребляет оба результата.runAfterBoth(CompletionStage<?>, Runnable)— выполняет действие после завершения обоих.
Пример
CompletableFuture<Double> priceFuture = CompletableFuture.supplyAsync(() -> getPrice());
CompletableFuture<Double> rateFuture = CompletableFuture.supplyAsync(() -> getExchangeRate());
CompletableFuture<Double> convertedPrice = priceFuture
.thenCombine(rateFuture, (price, rate) -> price * rate);
6. Выбор первого завершённого:
applyToEither(CompletionStage<T>, Function<T, U>)— применяет функцию к результату первого завершившегося.acceptEither(CompletionStage<T>, Consumer<T>)— потребляет результат первого завершившегося.runAfterEither(CompletionStage<?>, Runnable)— выполняет действие после завершения любого из двух.
7. Обработка ошибок:
exceptionally(Function<Throwable, T>)— обрабатывает исключение, возвращая значение по умолчанию.handle(BiFunction<T, Throwable, U>)— обрабатывает и результат, и исключение. Вызывается всегда.whenComplete(BiConsumer<T, Throwable>)— выполняет действие при завершении, не изменяя результат.
Пример
CompletableFuture<String> safe = CompletableFuture
.supplyAsync(() -> riskyOperation())
.handle((result, ex) -> {
if (ex != null) {
log.error("Ошибка", ex);
return "fallback";
}
return result;
});
8. Множественные CompletableFuture:
allOf(CompletableFuture<?>...)— возвращаетCompletableFuture<Void>, завершающийся, когда все переданные завершены.anyOf(CompletableFuture<?>...)— возвращаетCompletableFuture<Object>, завершающийся при завершении любого.
Код: сбор результатов через allOf
List<CompletableFuture<String>> futures = urls.stream()
.map(url -> CompletableFuture.supplyAsync(() -> fetch(url)))
.collect(Collectors.toList());
CompletableFuture<Void> allDone = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
// Собрать все результаты после завершения
List<String> results = allDone.thenApply(v ->
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
).join();
Ключевые различия:
| Метод | Аналог в Stream | Назначение |
|---|---|---|
thenApply |
map |
Синхронная трансформация |
thenCompose |
flatMap |
Асинхронная трансформация (разворачивает вложенный CF) |
thenCombine |
— | Объединение двух результатов |
allOf |
— | Ожидание всех |
anyOf |
— | Ожидание любого |
Каждый метод существует в трёх вариантах:
thenApply(fn)— выполняется в потоке, завершившем предыдущий шаг.thenApplyAsync(fn)— выполняется вForkJoinPool.commonPool().thenApplyAsync(fn, executor)— выполняется в указанном пуле.
Частые ошибки:
- Путаница между
thenApplyиthenCompose— приводит кCompletableFuture<CompletableFuture<T>>. allOfвозвращаетVoid— результаты нужно собирать отдельно из каждогоCompletableFuture.exceptionallyпослеhandle—handleуже обрабатывает исключение,exceptionallyполучитnull.
На собеседовании ключевой вопрос: «Чем отличается
thenApplyотthenCompose?» Запомните:thenApply— этоmap,thenCompose— этоflatMap. Если ваша функция сама возвращаетCompletableFuture, используйтеthenCompose, иначе получите вложенный future.