Асинхронный CompletableFuture. Часть 2 | OTUS

Курсы

Программирование
Подготовка к сертификации Oracle Java Programmer (OCAJP) Алгоритмы и структуры данных
-12%
Backend-разработчик на PHP
-8%
Web-разработчик на Python
-11%
Архитектура и шаблоны проектирования
-14%
Framework Laravel
-13%
iOS Developer. Basic
-23%
Разработчик на Spring Framework
-23%
Python Developer. Basic
-16%
C# ASP.NET Core разработчик
-18%
Разработчик программных роботов (RPA) на базе UiPath и PIX
-6%
Highload Architect
-9%
JavaScript Developer. Basic
-8%
Backend-разработка на Kotlin
-10%
JavaScript Developer. Professional
-9%
Android Developer. Basic
-10%
Unity Game Developer. Basic Программист С MS SQL Server Developer Cloud Solution Architecture Разработчик голосовых ассистентов и чат-ботов Vue.js разработчик VOIP инженер Программист 1С Flutter Mobile Developer Супер - интенсив по Kubernetes Advanced Fullstack JavaScript developer
Инфраструктура
PostgreSQL
-10%
Administrator Linux. Professional
-11%
Базы данных
-19%
Administrator Linux.Basic
-18%
Супер-интенсив «СУБД в высоконагруженных системах»
-18%
IoT-разработчик
-12%
Супер-интенсив "SQL для анализа данных"
-16%
Highload Architect
-9%
MS SQL Server Developer Безопасность Linux Cloud Solution Architecture Разработчик голосовых ассистентов и чат-ботов Внедрение и работа в DevSecOps Администратор Linux. Виртуализация и кластеризация Infrastructure as a code in Ansible Супер - интенсив по паттернам проектирования Супер - интенсив по Kubernetes
Специализации Курсы в разработке Подготовительные курсы
+7 499 938-92-02

Асинхронный CompletableFuture. Часть 2

Java_Deep_22.06_3_Site.png

У CompletableFuture есть ещё интересные функции. Например, надо построить цепочку из асинхронных вызовов. Т.е. после завершения первой асинхронной функции запустить вторую, после второй третью и т.д. В JavaScript для этого применяются promise. В Java можно использовать CompletableFuture.

Выглядит это так:

private void thenApply() throws ExecutionException, InterruptedException {
        final CompletableFuture<String> future = CompletableFuture.supplyAsync(()-> {
            System.out.println("job started");
            sleep(3);
            System.out.println("job done");
            return "feature done|";
        }).thenApply(result -> {
            System.out.println("applay result:" + result);
            return result + " applied";
        });
        System.out.println("waiting...");
        String result = future.get();
        System.out.println("finished, result:" + result);
    }

Сначала выполняется первая задача (sleep(3)) и только после её завершения запустится вторая задача. Причём второй задаче в качестве входного параметра можно передать результаты первой задачи.

А что делать с ошибками? Как их обрабатывать? У CompletableFuture есть встроенный механизм для обработки ошибок, применить его можно так:

private void thenApplyException() throws ExecutionException, InterruptedException {
        final CompletableFuture<String> future = CompletableFuture.supplyAsync(()-> {
            System.out.println("job started");
            sleep(3);
            throw new RuntimeException("runTime exception");
        }).thenApply(result -> {
            System.out.println("applay result:" + result);
            return result + " applied";
        }).exceptionally(exception -> {
            System.out.println("got exception, err:" + exception.getMessage());
            return exception.getMessage();
        });
        System.out.println("waiting...");
        String result = future.get();
        System.out.println("finished, result:" + result);
    }

(*) Обратите внимание на блок exceptionally, он запустится, если одна из задач выбросит исключение.

У CompletableFuture есть ещё интересная возможность: запускать несколько асинхронных задач, подождать, когда они завершатся и обработать полученные результаты.

Вот пример:

private void acceptBoth() {
        final CompletableFuture<String> futureOne = CompletableFuture.supplyAsync(()-> {
            System.out.println("job one started");
            sleep(3);
            System.out.println("job one is done");
            return "feature done one";
        });
        final CompletableFuture<String> futureTwo = CompletableFuture.supplyAsync(()-> {
            System.out.println("job two started");
            sleep(7);
            System.out.println("job two is done");
            return "feature done two";
        });
        System.out.println("waiting...");
        futureOne.thenAcceptBothAsync(futureTwo,
                (result1, result2) -> System.out.println("join:" + result1 + " " + result2));
        System.out.println("end");
    }

Обратите внимание на структуру futureOne.thenAcceptBothAsync. Ждём, когда завершатся обе задачи, и обрабатываем итоговый результат. В отличие от предыдущих примеров, в этом фрагменте кода поток выполнения программы не ждёт, когда завершатся future, а идёт дальше.

Вывод:

CompletableFuture из пакета java.util.concurrent предоставляет полезный и простой в использовании функционал, который помогает ускорить разработку и упростить код. В то же время надо помнить, что в java.util.concurrent много тонких моментов, требующих понимания и некоторой сноровки в использовании.

Есть вопрос? Напишите в комментариях!

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
1 комментарий
0

Спасибо, хорошая шпаргалка по CompletableFuture. А зачем такие короткие заметки разбивать на 2 части?

Для комментирования необходимо авторизоваться