Введение в CompletableFuture
Когда дело касается выполнения асинхронных задач, класс CompletableFuture является мощным инструментом для упрощения работы с многопоточностью и асинхронным программированием. Он позволяет строить цепочки задач, обрабатывать ошибки и объединять несколько будущих значений без использования блокирующих операций.
CompletableFuture — это расширение интерфейса Future, которое упрощает работу с асинхронными вычислениями. В отличие от стандартного Future, он позволяет:
▪️ Запускать задачи асинхронно;
▪️ Комбинировать несколько задач;
▪️ Обрабатывать ошибки без try-catch;
▪️ Строить цепочки зависимостей.
Пример создания асинхронной задачи:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// Эмуляция долгой задачи
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Задача завершена!";
});
System.out.println(future.join()); // Ожидание завершения и получение результата
Здесь задача выполняется в фоновом потоке, а основной поток продолжает свою работу.
🎮 Комбинирование нескольких задачЧасто нужно дождаться завершения нескольких задач и собрать их результаты. С помощью
thenCombine() можно комбинировать результаты нескольких асинхронных вычислений:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 50);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> result = future1.thenCombine(future2, Integer::sum);
System.out.println(result.join()); // 70
Здесь задачи выполняются параллельно, и их результаты комбинируются, когда обе завершены.
❌ Обработка ошибокОдним из главных преимуществ CompletableFuture является возможность обрабатывать ошибки без try-catch через метод
exceptionally():
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
if (true) throw new RuntimeException("Ошибка!");
return 42;
}).exceptionally(ex -> {
System.out.println("Произошла ошибка: " + ex.getMessage());
return 0; // Возвращаем дефолтное значение в случае ошибки
});
System.out.println(future.join()); // 0
Этот подход улучшает читаемость и упрощает обработку исключений в асинхронном коде.
📎 Композиция цепочекВы можете строить целые цепочки задач с помощью методов thenApply(), thenAccept() и т.д. Пример использования:
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(result -> result + " World")
.thenAccept(finalResult -> System.out.println(finalResult)); // Hello World
Каждая следующая задача запускается после завершения предыдущей, что позволяет гибко управлять зависимостями.
🛠 Применение на практикеCompletableFuture идеально подходит для задач, требующих асинхронности: запросы к API, обработка данных в фоне и т.д. Пример с вызовом нескольких API параллельно:
CompletableFuture<String> api1 = CompletableFuture.supplyAsync(() -> {
// Эмуляция запроса к первому API
return "Response from API 1";
});
CompletableFuture<String> api2 = CompletableFuture.supplyAsync(() -> {
// Эмуляция запроса ко второму API
return "Response from API 2";
});
CompletableFuture<Void> allOf = CompletableFuture.allOf(api1, api2);
allOf.thenRun(() -> {
try {
System.out.println(api1.get() + " & " + api2.get());
} catch (Exception e) {
e.printStackTrace();
}
});
@javalib #java