[JAVA] CompletableFuture Erste Schritte 3 (Versuchen Sie, eine asynchrone Anforderung auszugeben)

CompletableFuture Erste Schritte 2 (Versuchen Sie, eine CompletableFuture zu erstellen) Fortsetzung

Stellen Sie mehrere Anforderungen für die asynchrone Verarbeitung

Es gibt eine Liste von "String" wie unten, und jeder von diesen wird als Argument für die asynchrone Verarbeitung verwendet.

private static final List<String> argsList 
= Arrays.asList("test1", "test2", "test3", "test4");

Die asynchrone Verarbeitung wird mit "Stream" durchgeführt.

public static List<String> getDoubles() {
    List<CompletableFuture<String>> doubleFutures = argsList.stream()
            //Erstellen Sie eine abschließbare Zukunft mit einer Factory-Methode
            .map(arg -> CompletableFuture.supplyAsync(
                    () -> String.format("value: %f", doSomeLongComputation(arg))
            ))
            .collect(Collectors.toList());

    List<String> strs = doubleFutures.stream()
            //Verwenden Sie join, um das Ergebnis von Completable Future zu erhalten
            .map(CompletableFuture::join)
            .collect(Collectors.toList());
    return strs;
}

Es gibt zwei Punkte, die beachtet werden sollten. Beschrieben in Completable Future Getting Started 2, Verwenden der Factory-Methode "CompletableFuture.supplyAsync" Erstellen einer Liste von "CompletableFuture".

Wenn Sie das Verarbeitungsergebnis aus der erstellten Liste "CompletableFuture" abrufen, Sie verwenden die Methode "CompletableFuture.join".

Der Unterschied zwischen der get-Methode und der Join-Methode wird auf der folgenden Seite ausführlich erläutert. completablefuture join vs get Einfach ausgedrückt, wenn Sie die Join-Methode verwenden, müssen Sie für die Ausnahme keine explizite try-catch-Anweisung schreiben. (Es ist jedoch nicht ohne Ausnahmen, daher ist Vorsicht geboten.)

Beschreibung der kontinuierlichen asynchronen Verarbeitung

public static List<Double> getDoubleByTimes() {
    List<CompletableFuture<Double>> futures = argsList.stream() 
            .map(arg -> CompletableFuture.supplyAsync(
                    () -> doSomeLongComputation(arg)
            )) // Stream<CompletableFuture>Generieren Sie a
            //Übergeben Sie das Ergebnis der ersten CompletableFuture an die zweite CompletableFuture
            .map(future -> future.thenCompose(value ->
                    CompletableFuture.supplyAsync(
                            () -> timeLongComputation(value)))) 
            .collect(toList());
    return futures.stream()
            .map(CompletableFuture::join)
            .collect(toList());
}

Der Verarbeitungsablauf ist wie folgt

List<String> // stream()
↓
Stream<CompletableFuture<Double>> // CompletableFuture.supplyAsync
↓
Stream<CompletableFuture<Double>> // CompletableFuture.thenCompose
↓
List<CompletableFuture<Double>> // toList()

Mit thenCompose In der Kaskade können zwei verschiedene Completable Futures verwendet werden.

thenCompose(Function<? super T,? extends CompletionStage> fn)

Wenn diese Phase erfolgreich abgeschlossen wurde Gibt eine neue CompletionStage zurück, die mit diesem Bühnensatz als Argument für die angegebene Funktion ausgeführt wird. thenCompose@Oracle

Nennen Sie das "dann komponieren" der ersten "abschließbaren Zukunft" und Übergeben Sie das Ergebnis an die Funktion. Diese Funktion verwendet den vom ersten "CompletableFuture" zurückgegebenen Wert als Argument Der mit diesem Argument in der zweiten "CompletableFuture" berechnete Wert wird als Rückgabewert zurückgegeben.

Notizen (Unterschied zwischen dann Übernehmen und dann Verfassen)

Eine ähnliche Funktion ist thenApply.

thenApply(Function<? super T,? extends U> fn) Gibt eine neue CompletionStage zurück, die ausgeführt wird, wenn diese Phase erfolgreich abgeschlossen wurde. Das Ergebnis dieser Phase wird als Argument für die angegebene Funktion festgelegt. thenApply@Oracle

CompletableFuture | thenApply vs thenCompose

Die obige Erklärung von StackOverFlow ist detailliert. Insbesondere die folgenden Antworten waren sehr hilfreich.

thenApply() returned the nested futures as they were, but thenCompose() flattened the nested CompletableFutures so that it is easier to chain more method calls to it.

Wenn Sie im obigen Code thenApply verwenden, lautet der Rückgabewert: Es sieht aus wie "CompletableFuture <CompletableFuture >" Completable Future wird jetzt verschachtelt.

List<String> // stream()
↓
Stream<CompletableFuture<Double>> // CompletableFuture.supplyAsync
↓
Stream<CompletableFuture<CompletableFuture<Double>>> // CompletableFuture.thenApply
↓
List<CompletableFuture<CompletableFuture<Double>>> // toList()

In der Java 9-Dokumentation werden die folgenden Beispiele als ähnliche Konzepte angeführt: CompletableFuture.thenApply ⇄ Stream.map CompletableFuture.thenCompose ⇄ Stream.flatMap

Für Karte und flatMap, Zum Verständnis von Karte und Flatmap in Stream (1) Also habe ich es ein wenig zusammengefasst.

Bei Verwendung der Ergebnisse von zwei unabhängigen Completable Futures

thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)

Gibt eine neue CompletionStage zurück, die ausgeführt wird, wenn sowohl diese als auch die anderen angegebenen Phasen erfolgreich abgeschlossen wurden (Zur Laufzeit werden zwei Ergebnisse als Argumente für die angegebene Funktion verwendet).

List<CompletableFuture<Double>> futures = argsList.stream()
        .map(arg -> CompletableFuture.supplyAsync(
                () -> doSomeLongComputation(arg)
        ))
        .map(future -> future.thenCombine(
                CompletableFuture.supplyAsync(
                        () -> doSomeLongComputation("test")),
                (resultFromFirstFuture, resultFromSecondsFuture) -> resultFromFirstFuture * resultFromSecondsFuture
        ))
        .collect(toList());

return futures.stream()
        .map(CompletableFuture::join)
        .collect(toList());

notes: thenCombine / thenCombineAsync Wenn es ein Async-Suffix gibt, Das zweite Argument, "BiFunction", wird an den Thread-Pool und übergeben Es wird asynchron als andere Aufgabe ausgeführt.

reference Java8 In Action

Recommended Posts

CompletableFuture Erste Schritte 3 (Versuchen Sie, eine asynchrone Anforderung auszugeben)
CompletableFuture Erste Schritte 2 (Versuchen Sie, CompletableFuture zu erstellen)