HttpClient.sendAsync în Java

Trimite o cerere HTTP fără să aștepte: returnează imediat un CompletableFuture cu răspunsul.

Sintaxă
<T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest cerere, HttpResponse.BodyHandler<T> handler)

Ce face HttpClient.sendAsync

sendAsync este varianta asincronă a lui send: pornește cererea și revine imediat cu un CompletableFuture<HttpResponse<T>>. Poți continua lanțul (thenApply(HttpResponse::body)) și poți trimite mai multe cereri în paralel.

Pentru a aștepta toate cererile folosești CompletableFuture.allOf sau join() pe fiecare. Erorile de rețea nu se aruncă direct, ci ajung în future: le tratezi cu exceptionally sau le primești ca CompletionException la join().

Parametri

Parametru Descriere
cerere Cererea de trimis.
handler Cum se citește corpul răspunsului.

Ce returnează

Un CompletableFuture<HttpResponse<T>>.

Exemple

Exemplul 1: Trei cereri în paralel

Serverul răspunde la fiecare oraș după 300 ms. Cererile pornesc toate odată, deci așteptarea totală este de aproximativ 300 ms, nu 900 ms. Rezultatele se citesc în ordinea listei, deci afișarea este mereu aceeași.

import com.sun.net.httpserver.HttpServer;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
        server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
        server.createContext("/vreme", schimb -> {
            String oras = schimb.getRequestURI().getQuery();
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            byte[] corp = (oras + ": " + (oras.length() + 8) + "°C").getBytes(StandardCharsets.UTF_8);
            schimb.sendResponseHeaders(200, corp.length);
            try (OutputStream out = schimb.getResponseBody()) {
                out.write(corp);
            }
        });
        server.start();
        String baza = "http://127.0.0.1:" + server.getAddress().getPort() + "/vreme?";

        try (HttpClient client = HttpClient.newHttpClient()) {
            List<CompletableFuture<String>> cereri = List.of("Iași", "Cluj", "Constanța").stream()
                .map(oras -> HttpRequest.newBuilder(URI.create(baza + URLEncoder.encode(oras, StandardCharsets.UTF_8))).build())
                .map(cerere -> client.sendAsync(cerere, HttpResponse.BodyHandlers.ofString())
                    .thenApply(HttpResponse::body))
                .toList();
            cereri.forEach(c -> System.out.println(c.join()));
        }
        server.stop(0);
    }
}

Rezultat

Iași: 12°C
Cluj: 12°C
Constanța: 17°C

Exemplul 2: Eroare de rețea tratată cu exceptionally

Pe portul 9 (discard) nu ascultă nimeni, deci conectarea eșuează.

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) {
        try (HttpClient client = HttpClient.newHttpClient()) {
            HttpRequest cerere = HttpRequest.newBuilder(URI.create("http://127.0.0.1:9/")).build();
            String rezultat = client.sendAsync(cerere, HttpResponse.BodyHandlers.ofString())
                .thenApply(r -> "Stare " + r.statusCode())
                .exceptionally(e -> "Cererea a eșuat: " + e.getCause().getClass().getSimpleName())
                .join();
            System.out.println(rezultat);
        }
    }
}

Rezultat

Cererea a eșuat: ConnectException

Greșeli frecvente

  • Apelezi sendAsync(...).join() imediat, pentru fiecare cerere, într-o buclă. Cererile rulează una după alta; trimite-le întâi pe toate.
  • Uiți să aștepți future-urile înainte de sfârșitul lui main. Programul se poate opri înainte să sosească răspunsurile.

Comenzi înrudite

  • HttpClient.send: Trimite o cerere HTTP și așteaptă răspunsul; BodyHandlers.ofString() citește corpul ca text.
  • CompletableFuture.thenApply: thenApply transformă rezultatul când este gata (ca map); thenAccept îl folosește fără să returneze ceva (ca forEach).
  • CompletableFuture.allOf: Returnează un CompletableFuture care se termină când s-au terminat toate cele primite; anyOf, când s-a terminat primul.
  • HttpClient: Clientul HTTP din Java 11: newHttpClient() îl creează, send/sendAsync trimit cereri și primesc răspunsuri.