web-dev-qa-db-fra.com

CompletableFuture | thenApply vs thenCompose

Je ne comprends pas très bien la différence entre thenApply() et thenCompose().

Alors, quelqu'un pourrait-il fournir un cas d'utilisation valide?

À partir de la documentation Java:

thenApply(Function<? super T,? extends U> fn)

Retourne un nouveau CompletionStage qui, lorsque cette étape se termine normalement, est exécuté avec le résultat de cette étape en tant qu'argument de la fonction fournie.

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

Renvoie un nouveau CompletionStage qui, lorsque cette étape se termine normalement, est exécuté avec cette étape en tant qu'argument de la fonction fournie.

Je comprends que le deuxième argument de thenCompose étend le CompletionStage alors que thenApply ne le fait pas.

Quelqu'un pourrait-il donner un exemple, auquel cas je dois utiliser thenApply et quand thenCompose?

84
GuyT

thenApply est utilisé si vous avez une fonction de mappage synchrone.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenApply(x -> x+1);

thenCompose est utilisé si vous avez une fonction de mappage asynchrone (c'est-à-dire une fonction qui renvoie un CompletableFuture). Il renverra ensuite un avenir avec le résultat directement, plutôt qu'un avenir imbriqué.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
123
Joe C

Les Javadocs mis à jour dans Java 9 aideront probablement à mieux le comprendre:

thenApply

<U> CompletionStage<U> thenApply​(Function<? super T,? extends U> fn)

Renvoie une nouvelle CompletionStage qui, lorsque cette étape se termine normalement, est exécutée avec le résultat de cette étape en tant qu'argument de la fonction fournie.

Cette méthode est analogue à Optional.map et à Stream.map .

Consultez la documentation CompletionStage pour connaître les règles relatives à l'achèvement exceptionnel.

thenCompose

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

Retourne un nouveau CompletionStage qui est complété avec la même valeur que le CompletionStage renvoyé par la fonction donnée.

Lorsque cette étape se termine normalement, la fonction donnée est invoquée avec le résultat de cette étape comme argument, renvoyant un autre CompletionStage. Lorsque cette étape se termine normalement, le CompletionStage renvoyé par cette méthode est complété avec la même valeur.

Pour garantir les progrès, la fonction fournie doit organiser la réalisation éventuelle de son résultat.

Cette méthode est analogue à Optional.flatMap et à Stream.flatMap .

Consultez la documentation CompletionStage pour connaître les règles relatives à l'achèvement exceptionnel.

38
Didier L

Je pense que la réponse postée par @Joe C est trompeuse.

Laissez-moi essayer d’expliquer la différence entre thenApply et thenCompose avec un exemple.

Supposons que nous ayons 2 méthodes: getUserInfo(int userId) et getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> userInfo = getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

Les deux types de retour de méthode sont CompletableFuture.

Nous voulons d'abord appeler getUserInfo() puis, une fois l'opération terminée, appeler getUserRating() avec le UserInfo résultant.

À la fin de la méthode getUserInfo(), essayons à la fois thenApply et thenCompose. La différence est dans les types de retour:

CompletableFuture<CompletableFuture<UserRating>> f =
    userInfo.thenApply(this::getUserRating);

CompletableFuture<UserRating> relevanceFuture =
    userInfo.thenCompose(this::getUserRating);

thenCompose() fonctionne comme Scala flatMap qui aplatit les futurs imbriqués.

thenApply() a renvoyé les futurs imbriqués tels qu'ils étaient, mais thenCompose() aplatit le CompletableFutures imbriqué afin qu'il soit plus facile de chaîner davantage d'appels de méthode.

31
Dorjee

thenApply et thenCompose est appelé sur un CompletableFuture et fait quelque chose avec son résultat en fournissant un Function. thenApply et thenCompose renvoient chacun un CompletableFuture comme leur propre résultat, de sorte que vous puissiez chaîner plusieurs thenApply ou thenCompose, chacun ayant un Function faisant quelque chose pour le dernier Function.

Ce Function doit parfois faire quelque chose de manière synchrone et renvoyer un résultat, auquel cas thenApply doit être utilisé.

CompletableFuture.completedFuture(1)
    .thenApply((x)->x+1) // adding one to the result synchronously
    .thenApply((x)->System.println(x));

Vous pouvez également faire quelque chose d'asynchrone dans cette Function, et cette chose asynchrone que vous faites devrait retourner un CompletionStage. Le prochain Function de la chaîne ne souhaite pas obtenir un CompletionStage en entrée, mais plutôt le résultat de ce CompletionStage. Donc, alors vous devriez utiliser thenCompose.

// addOneAsync may be implemented by using another thread, or calling a remote method
// CompletableFuture<Integer> addOneAsync(int input);
CompletableFuture.completedFuture(1)
    .thenCompose((x)->addOneAsync(x)) // doing something asynchronous
    .thenApply((x)->System.println(x));

En Javascript, Promise.then peut accepter une fonction qui renvoie soit une valeur, soit un Promise d'une valeur. Dans Java à cause des règles de type, les deux fonctions doivent être typées séparément, c'est-à-dire. (Function<? super T,? extends U> fn et Function<? super T,? extends CompletionStage<U>> fn. (ou que Java doit faire une vérification de type pour faire quelque chose de spécial si vous retournez un CompletionStage, mais ils ont choisi l'ancien) Le résultat final étant, Promise.then est implémenté en deux parties thenApply et thenCompose .

Vous pouvez également lire ma réponse à propos de thenApplyAsync si cela vous confond.

7
1283822