CompletableFuture | thenApply vs thenCompose


119

thenApply() Ve arasındaki farkı anlayamıyorum thenCompose().

Öyleyse, birisi geçerli bir kullanım durumu sağlayabilir mi?

Java belgelerinden:

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

CompletionStageBu aşama normal olarak tamamlandığında, sağlanan işlevin argümanı olarak bu aşamanın sonucuyla çalıştırılan yeni bir döndürür .

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

CompletionStageBu aşama normal olarak tamamlandığında, sağlanan işlevin argümanı olarak bu aşama ile çalıştırılan yeni bir döndürür .

Anladım ki 2. argüman thenComposeCompletionStage'i uzatmıyor thenApply.

Birisi hangi durumda thenApplyve ne zaman kullanmam gerektiği konusunda bir örnek verebilir thenComposemi?


39
Eğer arasındaki farkı anlamak mı mapve flatMapde Stream? thenApplyolduğu mapve thenComposeolduğu flatMapbir CompletableFuture. Sahip thenComposeolmaktan kaçınmak için kullanırsın CompletableFuture<CompletableFuture<..>>.
Misha

2
@Misha Yorumunuz için teşekkürler. Evet, mapve arasındaki farkı biliyorum, ne demek istediğini flatMapanladım. Tekrar teşekkürler :)
GuyT

Bu CompletableFuture ile başlamak için çok güzel bir rehber - baeldung.com/java-completablefuture
thealchemist

Yanıtlar:


168

thenApply Senkronize bir eşleme fonksiyonunuz varsa kullanılır.

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

thenComposeEşzamansız bir eşleme işleviniz varsa (yani a döndüren bir işlev CompletableFuture) kullanılır. Daha sonra, iç içe geçmiş bir gelecek yerine doğrudan sonucu olan bir gelecek döndürecektir.

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

14
Bir programcı neden .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))bunun yerine kullanmalı .thenApplyAsync(x -> x+1)? Eşzamanlı veya eşzamansız olmak önemli bir fark değildir .
Holger

17
Bunu sevmezlerdi. Ancak, kullandıkları üçüncü taraf bir kitaplık a döndürdüyse , yapıyı düzleştirmenin yolu CompletableFuturebu olacaktır thenCompose.
Joe C

1
@ArunavSanyal, oylar farklı bir resim gösteriyor. Bu cevap açık ve özdür.
Alex Shesterov

@Holger kafan karıştıysa diğer cevabımı oku thenApplyAsyncçünkü düşündüğün gibi değil.
1283822

@ 1283822 Kafamın karıştığını düşünmeni sağlayan şeyin ne olduğunu bilmiyorum ve cevabında "düşündüğün gibi değil" iddianı destekleyen hiçbir şey yok.
Holger

49

@ Joe C tarafından gönderilen yanıtın yanıltıcı olduğunu düşünüyorum.

Beni arasındaki farkı anlatmaya çalışayım thenApplyve thenComposebir örnekle.

Diyelim ki 2 yöntemimiz var: getUserInfo(int userId)ve getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

Her iki yöntem dönüş türü de CompletableFuture.

Biz aramak istediğiniz getUserInfo()ilk ve onun tamamlanması üzerine, çağrı getUserRating()çıkan ile UserInfo.

getUserInfo()Yöntemin tamamlanması üzerine , ikisini de deneyelim thenApplyve thenCompose. Aradaki fark iade türlerindedir:

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

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

thenCompose()İç içe geçmiş gelecekleri düzleştiren Scala'nınkiflatMap gibi çalışır .

thenApply()yuvalanmış futures'ları oldukları gibi döndürdü, ancak thenCompose()iç içe geçmişleri düzleştirdi, CompletableFuturesböylece ona daha fazla yöntem çağrısı zincirlemek daha kolay oldu.


2
O halde Joe C'nin cevabı yanıltıcı değil. Doğru ve daha özlü.
koleS

2
Bu çok yardımcı oldu :-)
dstibbe

1
CompletableFuture <UserInfo> getUserInfo ve CompletableFuture <UserRating> getUserRating (UserInfo) \\ yazmak kötü bir tasarım olsa da, bunun yerine UserInfo getUserInfo () ve int getUserRating (UserInfo), eğer eşzamansız ve zincir kullanmak istiyorsam, o zaman yapabilirim ompletableFuture.supplyAsync (x => getUserInfo (userId)). thenApply (userInfo => getUserRating (userInfo)) veya bunun gibi herhangi bir şey kullanın, bu daha okunabilir imho ve TÜM dönüş türlerini CompletableFuture
user1694306

@ user1694306 Tasarımın kötü olup olmadığı, kullanıcı derecelendirmesinin içinde yer almasına UserInfo(o zaman evet) veya ayrı olarak, hatta belki maliyetli (o zaman hayır) alınıp alınmamasına bağlıdır .
glglgl

42

Java 9'daki güncellenmiş Javadocs muhtemelen daha iyi anlamanıza yardımcı olacaktır:

thenApply

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

CompletionStageBu aşama normal olarak tamamlandığında, sağlanan işlevin argümanı olarak bu aşamanın sonucuyla çalıştırılan yeni bir döndürür .

Bu yöntem, Optional.mapve ile benzerdir Stream.map.

CompletionStageİstisnai tamamlamayı kapsayan kurallar için belgelere bakın .

thenCompose

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

Verilen işlev tarafından döndürülenle CompletionStageaynı değerle tamamlanmış yeni bir döndürür CompletionStage.

Bu aşama normal olarak tamamlandığında, verilen işlev, bu aşamanın sonucu argüman olarak çağrılır ve başka bir sonuç döndürülür CompletionStage. Bu aşama normal olarak tamamlandığında, CompletionStagebu yöntemle döndürülen aynı değerle tamamlanır.

İlerlemeyi sağlamak için sağlanan işlev, sonucun nihai tamamlanmasını ayarlamalıdır.

Bu yöntem, Optional.flatMapve ile benzerdir Stream.flatMap.

CompletionStageİstisnai tamamlamayı kapsayan kurallar için belgelere bakın .


14
Onlar bu işlevleri adını vermedi Acaba neden mapve flatMapilk etapta.
Matthias Braun

1
@MatthiasBraun Sanırım bunun nedeni thenApply()basitçe arayacak Function.apply()ve thenCompose()işlevlerin oluşturulmasına biraz benziyor.
Didier L

14

thenApplyve thenComposeyöntemleridir CompletableFuture. A CompleteableFutureile sonucunu elde etmek için bir şey yapmak istediğinizde bunları kullanın Function.

thenApplyve thenComposeher ikisi de CompletableFuturekendi sonucu olarak a döndürür . Birden çok thenApplyveya thenComposebirlikte zincirleyebilirsiniz . Bir tedarik Functionedilen sonuç bir sonraki giriş olacak Her arama için Function.

FunctionBazen verilen eşzamanlı bir şeyler yapmak gerekiyor. Dönüş türünüzün türü Functionolmayan olmalıdır Future. Bu durumda kullanmalısınız thenApply.

CompletableFuture.completedFuture(1)
    .thenApply((x)->x+1) // adding one to the result synchronously, returns int
    .thenApply((y)->System.println(y)); // value of y is 1 + 1 = 2

Diğer zamanlarda bunun içinde eşzamansız işlem yapmak isteyebilirsiniz Function. Bu durumda kullanmalısınız thenCompose. Dönüş türünüz Functionbir CompletionStage. FunctionZincirdeki bir sonraki , bunun sonucunu CompletionStagegirdi olarak alacak , böylece CompletionStage.

// addOneAsync may be implemented by using another thread, or calling a remote method
abstract CompletableFuture<Integer> addOneAsync(int input);

CompletableFuture.completedFuture(1)
    .thenCompose((x)->addOneAsync(x)) // doing something asynchronous, returns CompletableFuture<Integer>
    .thenApply((y)->System.println(y)); // y is an Integer, the result of CompletableFuture<Integer> above

Bu, Javascript'inkine benzer bir fikirdir Promise. Promise.thenbir değer veya bir değer döndüren bir işlevi kabul edebilir Promise. Bu iki yöntemin Java'da farklı isimlere sahip olmasının nedeni, genel silme işleminden kaynaklanmaktadır . Function<? super T,? extends U> fnve Function<? super T,? extends CompletionStage<U>> fnaynı Runtime türü olarak kabul edilir - Function. Bu nedenle thenApplyve thenComposeaçıkça adlandırılması gerekir, yoksa Java derleyicisi aynı yöntem imzalarından şikayet ederdi. Sonuçta varlık, JavaScript adlı Promise.theniki parça halinde uygulanmaktadır - thenApplyve thenCompose- Java.

İlgili işlev hakkında kafanız karışırsa diğer cevabımı da okuyabilirsiniz thenApplyAsync.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.