CompletableFuture Erste Schritte 2 (Versuchen Sie, eine CompletableFuture zu erstellen) Fortsetzung
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.)
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.
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
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.
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