CompletableFuture | laluTerapkan vs lalu Tulis

119

Saya tidak bisa memahami perbedaan antara thenApply() dan thenCompose().

Jadi, dapatkah seseorang memberikan kasus penggunaan yang valid?

Dari dokumen Java:

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

Mengembalikan yang baru CompletionStage, ketika tahapan ini selesai secara normal, dieksekusi dengan hasil tahapan ini sebagai argumen ke fungsi yang disediakan.

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

Mengembalikan yang baru CompletionStage, ketika tahap ini selesai secara normal, dijalankan dengan tahap ini sebagai argumen ke fungsi yang disediakan.

Saya mengerti bahwa argumen ke-2 thenComposememperluas CompletionStage di mana thenApplytidak.

Bisakah seseorang memberikan contoh dalam hal apa saya harus menggunakan thenApplydan kapan thenCompose?

GuyT
sumber
39
Apakah Anda memahami perbedaan antara mapdan flatMapdalam Stream? thenApplyadalah mapdan thenComposeadalah flatMapdari CompletableFuture. Anda gunakan thenComposeuntuk menghindari CompletableFuture<CompletableFuture<..>>.
Misha
2
@Misha Terima kasih atas komentar Anda. Ya, saya tahu perbedaan antara mapdan flatMapdan saya mengerti maksud Anda. Terima kasih lagi :)
GuyT
Ini adalah panduan yang sangat bagus untuk memulai dengan CompletableFuture - baeldung.com/java-completablefuture
thealchemist

Jawaban:

168

thenApply digunakan jika Anda memiliki fungsi pemetaan sinkron.

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

thenComposedigunakan jika Anda memiliki fungsi pemetaan asinkron (yaitu fungsi yang mengembalikan a CompletableFuture). Ini kemudian akan mengembalikan masa depan dengan hasil secara langsung, daripada masa depan bersarang.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
Joe C
sumber
14
Mengapa seorang programmer harus menggunakan .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))bukan .thenApplyAsync(x -> x+1)? Menjadi sinkron atau asinkron bukanlah perbedaan yang relevan.
Holger
17
Mereka tidak akan melakukannya seperti itu. Namun, jika pustaka pihak ketiga yang mereka gunakan mengembalikan a CompletableFuture, maka ini akan menjadi thenComposecara untuk meratakan struktur.
Joe C
1
@ArunavSanyal, hasil voting menunjukkan gambaran berbeda. Jawaban ini jelas dan ringkas.
Alex Shesterov
@Holger membaca jawaban saya yang lain jika Anda bingung thenApplyAsynckarena bukan itu yang Anda pikirkan.
1283822
@ 1283822 Saya tidak tahu apa yang membuat Anda berpikir bahwa saya bingung dan tidak ada jawaban Anda yang mendukung klaim Anda bahwa "bukan itu yang Anda pikirkan".
Holger
49

Saya pikir jawaban yang diposting oleh @Joe C menyesatkan.

Izinkan saya mencoba menjelaskan perbedaan antara thenApplydan thenComposedengan contoh.

Misalkan kita memiliki 2 metode: getUserInfo(int userId)dan getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

Kedua tipe pengembalian metode tersebut CompletableFuture.

Kami ingin menelepon getUserInfo()dulu, dan setelah selesai, panggil getUserRating()dengan hasil UserInfo.

Setelah menyelesaikan getUserInfo()metode, mari kita coba thenApplydan thenCompose. Perbedaannya terletak pada tipe pengembalian:

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

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

thenCompose()bekerja seperti ScalaflatMap yang meratakan masa depan bersarang.

thenApply()mengembalikan masa depan bersarang seperti sebelumnya, tetapi thenCompose()meratakannya CompletableFuturessehingga lebih mudah untuk merangkai lebih banyak panggilan metode ke sana.

Dorjee
sumber
2
Maka jawaban Joe C tidak menyesatkan. Itu benar dan lebih ringkas.
koleS
2
Ini sangat membantu :-)
dstibbe
1
Imho itu adalah desain yang buruk untuk menulis CompletableFuture <UserInfo> getUserInfo dan CompletableFuture <UserRating> getUserRating (UserInfo) \\ sebagai gantinya harus UserInfo getUserInfo () dan int getUserRating (UserInfo) jika saya ingin menggunakannya async dan chain, maka saya bisa gunakan ompletableFuture.supplyAsync (x => getUserInfo (userId)). thenApply (userInfo => getUserRating (userInfo)) atau semacamnya, itu lebih mudah dibaca imho, dan tidak wajib membungkus SEMUA jenis kembalian ke CompletableFuture
user1694306
@ user1694306 Apakah itu desain yang buruk atau tidak tergantung pada apakah peringkat pengguna terdapat dalam UserInfo(maka ya) atau apakah harus diperoleh secara terpisah, bahkan mungkin mahal (lalu tidak).
glglgl
42

Javadocs yang diperbarui di Java 9 mungkin akan membantu memahaminya dengan lebih baik:

laluLamar

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

Mengembalikan yang baru CompletionStage, ketika tahapan ini selesai secara normal, dieksekusi dengan hasil tahapan ini sebagai argumen ke fungsi yang disediakan.

Metode ini analog dengan Optional.mapdan Stream.map.

Lihat CompletionStagedokumentasi untuk aturan yang mencakup penyelesaian luar biasa.

laluCompose

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

Mengembalikan nilai baru CompletionStageyang diselesaikan dengan nilai yang sama seperti yang CompletionStagedikembalikan oleh fungsi yang diberikan.

Ketika tahapan ini selesai secara normal, fungsi yang diberikan dipanggil dengan hasil tahapan ini sebagai argumen, mengembalikan yang lain CompletionStage. Ketika tahapan itu selesai secara normal, pengembalian CompletionStagedengan metode ini diselesaikan dengan nilai yang sama.

Untuk memastikan kemajuan, fungsi yang disediakan harus mengatur penyelesaian akhirnya dari hasilnya.

Metode ini analog dengan Optional.flatMapdan Stream.flatMap .

Lihat CompletionStagedokumentasi untuk aturan yang mencakup penyelesaian luar biasa.

Didier L
sumber
14
Saya bertanya-tanya mengapa mereka tidak menamai fungsi-fungsi itu mapdan flatMapdi tempat pertama.
Matthias Braun
1
@MatthiasBraun Saya pikir itu karena thenApply()hanya akan memanggil Function.apply(), dan thenCompose()agak mirip dengan fungsi penulisan.
Didier L
14

thenApplydan thenComposemerupakan metode CompletableFuture. Gunakan jika Anda berniat melakukan sesuatu untuk mendapatkan CompleteableFuturehasil dengan a Function.

thenApplydan thenComposekeduanya mengembalikan CompletableFuturesebagai hasil mereka sendiri. Anda dapat merantai beberapa thenApplyatau thenComposebersama. Berikan a Functionke setiap panggilan, yang hasilnya akan menjadi masukan ke panggilan berikutnya Function.

Yang FunctionAnda berikan terkadang perlu melakukan sesuatu secara sinkron. Tipe pengembalian Anda Functionharus non- Futuretipe. Dalam hal ini Anda harus menggunakan 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 lain waktu Anda mungkin ingin melakukan pemrosesan asinkron dalam hal ini Function. Dalam hal ini Anda harus menggunakan thenCompose. Jenis pengembalian Anda Functionharus a CompletionStage. FunctionRantai berikutnya akan mendapatkan hasil CompletionStagesebagai masukan, sehingga membuka bungkus 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

Ini adalah ide yang mirip dengan Javascript Promise. Promise.thendapat menerima fungsi yang mengembalikan nilai atau Promisenilai. Alasan mengapa kedua metode ini memiliki nama yang berbeda di Java adalah karena penghapusan yang umum . Function<? super T,? extends U> fndan Function<? super T,? extends CompletionStage<U>> fndianggap sebagai jenis Runtime yang sama - Function. Jadi thenApplydan thenComposeharus diberi nama yang berbeda, atau kompiler Java akan mengeluh tentang tanda tangan metode yang identik. Hasil akhirnya adalah, Javascript Promise.thendiimplementasikan dalam dua bagian - thenApplydanthenCompose - di Java.

Anda dapat membaca jawaban saya yang lain jika Anda juga bingung tentang fungsi terkait thenApplyAsync.

1283822
sumber