Асинхронные HTTP запросы в Java: аналог async/await в C#

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

В Java аналогом async/await из C# служит класс CompletableFuture. Он обеспечивает реализацию асинхронности и бесблокирующих операций. Возьмем такой пример:

Java
Скопировать код
CompletableFuture.supplyAsync(() -> "LongRunningMethod()")
    .thenApplyAsync(result -> "Process(" + result + ")")
    .thenAccept(System.out::println)
    .exceptionally(e -> { System.err.println(e); return null; });

Здесь мы выполняем асинхронную операцию, затем обрабатываем результат, и, в конце, выводим его с помощью System.out.println. Этот процесс напоминает применение async/await в C#. В случае ошибки используем метод exceptionally.

Кинга Идем в IT: пошаговый план для смены профессии

Почему нужно использовать асинхронное программирование?

Использование асинхронности в Java (и не только) предоставляет нам ряд преимуществ. Она позволяет проводить бесблокирующие операции, улучшает масштабируемость, упрощает сложные операции и обеспечивает эффективную обработку исключений.

Выясним, что такое CompletableFuture

Мастерство создания цепочек

В Java также можно создавать цепочки асинхронных задач с использованием методов thenApply, thenCompose, thenCombine:

Java
Скопировать код
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
    Thread.sleep(5000); // Эмуляция долгосрочной операции
    return "Результат операции";
});

completableFuture
    .thenApply(result -> result + " – обработан")
    .thenAccept(System.out::println); // Печатаем результат

Управление потоками исполнения

С помощью ExecutorService мы можем эффективно управлять потоками при работе с CompletableFuture:

Java
Скопировать код
ExecutorService executor = Executors.newFixedThreadPool(10);

CompletableFuture.supplyAsync(() -> "Задача с отдельным исполнителем", executor)
    .thenAcceptAsync(System.out::println, executor);

executor.shutdown();

Синхронное управление задачами

Метод CompletableFuture.allOf позволяет обработать сразу несколько задач:

Java
Скопировать код
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2, future3);
combinedFuture.get();

Альтернативы и добавочные инструменты

Несмотря на то, что CompletableFuture довольно функционален, он не полностью воспроизводит async/await. В этом контексте полезно рассмотреть:

  • RxJava: предлагает операторы для асинхронного и событийного программирования.
  • Quasar: волокна, используемые как более легковесная альтернатива потокам.
  • AsyncHttpClient и AsyncCompletionHandler: предназначены для асинхронных HTTP-операций.
  • JavaFlow и библиотека Continuations: имитируют синтаксис async/await.

Неизведанные территории

RxJava

В реактивном программировании с RxJava используется такая конструкция:

Java
Скопировать код
Observable<String> observable = ...; // Формируем Observable
observable
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.single())
    .subscribe(System.out::println);

Волокна от Quasar

Волокна Quasar позволяют достичь параллелизма с меньшими затратами:

Java
Скопировать код
Fiber<Void> fiber = new Fiber<>(() -> {
    System.out.println("Внутри волокна");
    return null;
}).start();

Асинхронный ввод/вывод

В Java асинхронный I/O можно реализовать с помощью AsynchronousFileChannel и других подобных классов:

Java
Скопировать код
Path file = Paths.get("file.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(file, READ, WRITE);

ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
    public void completed(Integer result, ByteBuffer attachment) {
        System.out.println("Чтение завершено!");
    }

    public void failed(Throwable exc, ByteBuffer attachment) {
        // Обрабатываем ошибку
    }
});

Визуализация

Входение в структуру аналога async/await из Java через метафору оркестра:

Markdown
Скопировать код
C# async/await:
🎼: "Вступление!" 🕒 -> 🎻(async) + 🎷(async) + 🥁(await) -> 🎼: "Гармония!"

Java с CompletableFuture:
🎼: "Создаём мелодию!" 🕒 -> 🎻(supplyAsync) + 🎷(thenCompose) -> 🎼: "Заключение!"

Примеры из практики

Асинхронные HTTP-запросы с использованием AsyncHttpClient

Java
Скопировать код
AsyncHttpClient client = Dsl.asyncHttpClient();

client.prepareGet("https://example.com")
    .execute()
    .toCompletableFuture()
    .thenApply(Response::getResponseBody)
    .thenAccept(System.out::println)
    .join();

client.close();

Лямбда-выражения

Лямбды позволяют упростить код:

Java
Скопировать код
CompletableFuture.supplyAsync(() -> {
    return "Такой вот лямбда-выражение.";
}).thenAccept(joke -> System.out.println("Шутка: " + joke));

Управление состояниями в асинхронном режиме

CompletableFuture поддерживает переходы между состояниями.

GitHub-репозитории с конкретными примерами

Изучайте применение асинхронного программирования на основе реальных примеров из GitHub:

Полезные материалы

  1. CompletableFuture (Java Platform SE 8 ) — официальная документация по CompletableFuture в Java.
  2. Асинхронное программирование в Java: понятия и принципы — обстоятельное руководство по теме асинхронности.
  3. Асинхронное программирование в Java 8: как использовать CompletableFuture — практическое введение в работу с CompletableFuture.
  4. Введение в CompletableFuture в Java 8 – YouTube — лекция Венката Субраманиама о CompletableFuture.
  5. Знакомимся с Reactive Streams API в Java — вводный материал по асинхронной обработке данных с применением Reactive Streams API.
  6. Различия между CompletableFuture и CompletionStage — рассматриваются отличия и специфичные аспекты применения CompletableFuture и CompletionStage.