CompletableFuture.thenCombine în Java

Combină rezultatele a două CompletableFuture independente, după ce sunt gata amândouă.

Sintaxă
<U, V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> altul, BiFunction<? super T, ? super U, ? extends V> functie)

Ce face CompletableFuture.thenCombine

a.thenCombine(b, (x, y) -> ...) așteaptă ca a și b să se termine, apoi apelează funcția cu cele două rezultate. Cele două sarcini rulează în paralel, deci timpul total este cel al sarcinii mai lente, nu suma lor.

Dacă oricare dintre cele două eșuează, rezultatul combinat eșuează și el, iar funcția nu mai este apelată. Pentru mai mult de două sarcini folosește CompletableFuture.allOf.

Parametri

Parametru Descriere
altul Al doilea CompletableFuture.
functie Primește cele două rezultate și returnează rezultatul combinat.

Ce returnează

Un nou CompletableFuture<V> cu rezultatul funcției.

Exemple

Exemplul 1: Prețul în euro: produs și curs, cerute în paralel

Cele două cereri durează câte 300 ms și rulează în același timp; thenCombine le folosește pe amândouă când sunt gata.

import java.util.concurrent.CompletableFuture;

public class Main {
    static <T> T dupa(int ms, T valoare) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return valoare;
    }

    public static void main(String[] args) {
        CompletableFuture<Double> pretLei = CompletableFuture.supplyAsync(() -> dupa(300, 2499.0));
        CompletableFuture<Double> cursEuro = CompletableFuture.supplyAsync(() -> dupa(300, 4.97));
        String text = pretLei
            .thenCombine(cursEuro, (lei, curs) -> String.format("Laptop: %.2f lei = %.2f euro", lei, lei / curs))
            .join();
        System.out.println(text);
    }
}

Rezultat

Laptop: 2499.00 lei = 502.82 euro

Exemplul 2: Nume și notă, apoi un mesaj

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        CompletableFuture<String> nume = CompletableFuture.supplyAsync(() -> "Ioana");
        CompletableFuture<Integer> nota = CompletableFuture.supplyAsync(() -> 10);
        nume.thenCombine(nota, (n, x) -> n + " a luat nota " + x + " la matematică")
            .thenAccept(System.out::println)
            .join();
    }
}

Rezultat

Ioana a luat nota 10 la matematică

Exemplul 3: Dacă una eșuează, combinarea eșuează

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

public class Main {
    public static void main(String[] args) {
        CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> 100);
        CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> Integer.parseInt("x"));
        try {
            a.thenCombine(b, Integer::sum).join();
        } catch (CompletionException e) {
            System.out.println("Combinarea a eșuat: " + e.getCause().getClass().getSimpleName());
        }
    }
}

Rezultat

Combinarea a eșuat: NumberFormatException

Greșeli frecvente

  • Faci a.join() și abia apoi pornești b. Sarcinile rulează una după alta; pornește-le pe amândouă, apoi combină-le.
  • Folosești thenCombine când a doua sarcină are nevoie de rezultatul primei. Atunci folosește thenCompose.

Comenzi înrudite

  • CompletableFuture.allOf: Returnează un CompletableFuture care se termină când s-au terminat toate cele primite; anyOf, când s-a terminat primul.
  • CompletableFuture.thenApply: thenApply transformă rezultatul când este gata (ca map); thenAccept îl folosește fără să returneze ceva (ca forEach).
  • CompletableFuture.supplyAsync: Pornește o sarcină pe alt fir și returnează imediat un CompletableFuture; supplyAsync are rezultat, runAsync nu.
  • CompletableFuture: Un Future care se poate înlănțui: definești ce se întâmplă cu rezultatul (thenApply, thenAccept) fără să aștepți; join() îl ia la final.