Hoàn thiện | thenApply so với thenCompose


119

Tôi không thể hiểu được sự khác biệt giữa thenApply() và thenCompose().

Vì vậy, ai đó có thể cung cấp một trường hợp sử dụng hợp lệ?

Từ tài liệu Java:

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

Trả về một giá trị mới CompletionStage, khi giai đoạn này hoàn thành bình thường, được thực thi với kết quả của giai đoạn này là đối số của hàm được cung cấp.

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

Trả về một giá trị mới CompletionStage, khi giai đoạn này hoàn thành bình thường, được thực thi với giai đoạn này làm đối số cho hàm được cung cấp.

Tôi nhận được rằng đối số thứ hai của thenComposemở rộng CompletionStage thenApplykhông.

Ai đó có thể cung cấp một ví dụ trong trường hợp nào tôi phải sử dụng thenApplyvà khi thenComposenào không?


39
Bạn có hiểu sự khác biệt giữa mapflatMaptrong Streamkhông? thenApplymapthenComposeflatMapcủa CompletableFuture. Bạn sử dụng thenComposeđể tránh có CompletableFuture<CompletableFuture<..>>.
Misha

2
@Misha Cảm ơn bạn đã bình luận. Vâng, tôi biết sự khác biệt giữa mapflatMapvà tôi hiểu ý bạn. Cảm ơn một lần nữa :)
GuyT

Đây là một hướng dẫn rất hay để bắt đầu với CompletableFuture - baeldung.com/java-completablefuture
thealchemist

Câu trả lời:


168

thenApply được sử dụng nếu bạn có chức năng ánh xạ đồng bộ.

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

thenComposeđược sử dụng nếu bạn có một hàm ánh xạ không đồng bộ (tức là một hàm trả về a CompletableFuture). Sau đó, nó sẽ trả về một tương lai với kết quả trực tiếp, thay vì một tương lai lồng vào nhau.

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

14
Tại sao một lập trình viên nên sử dụng .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))thay vì .thenApplyAsync(x -> x+1)? Đồng bộ hay không đồng bộ không phải là sự khác biệt có liên quan.
Holger

17
Họ sẽ không làm như vậy. Tuy nhiên, nếu thư viện của bên thứ ba mà họ đã sử dụng trả về a CompletableFuture, thì đây sẽ là thenComposecách để làm phẳng cấu trúc.
Joe C

1
@ArunavSanyal, các phiếu bầu cho thấy một bức tranh khác. Câu trả lời này rõ ràng và ngắn gọn.
Alex Shesterov

@Holger đọc câu trả lời khác của tôi nếu bạn bối rối thenApplyAsyncvì nó không như bạn nghĩ.
1283822,

@ 1283822 Tôi không biết điều gì khiến bạn nghĩ rằng tôi đã nhầm lẫn và không có gì trong câu trả lời của bạn ủng hộ tuyên bố của bạn rằng "nó không phải như những gì bạn nghĩ".
Holger

49

Tôi nghĩ câu trả lời được đăng bởi @Joe C là gây hiểu lầm.

Hãy để tôi cố gắng giải thích sự khác biệt giữa thenApplythenComposevới một ví dụ.

Giả sử rằng chúng ta có 2 phương pháp: getUserInfo(int userId)getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

Cả hai kiểu trả về phương thức là CompletableFuture.

Chúng tôi muốn gọi getUserInfo()trước và khi hoàn thành, hãy gọi getUserRating()kèm theo kết quả UserInfo.

Khi hoàn thành getUserInfo()phương thức, hãy thử cả hai thenApplythenCompose. Sự khác biệt là ở các loại trả lại:

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

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

thenCompose()hoạt động giống như ScalaflatMap làm phẳng các tương lai lồng ghép.

thenApply()đã trả lại các tương lai lồng nhau như cũ, nhưng thenCompose()làm phẳng các tương lai lồng nhau CompletableFuturesđể dễ dàng chuỗi các lệnh gọi phương thức hơn đến nó.


2
Vậy thì câu trả lời của Joe C không hề sai lệch. Nó là chính xác và ngắn gọn hơn.
koleS

2
Đây là rất hữu ích :-)
dstibbe

1
Tôi thấy việc viết CompletableFuture <UserInfo> getUserInfo và CompletableFuture <UserRating> getUserRating (UserInfo) là một thiết kế kém, thay vào đó nó phải là UserInfo getUserInfo () và int getUserRating (UserInfo) nếu tôi muốn sử dụng nó không đồng bộ và chuỗi, thì tôi có thể sử dụng ompletableFuture.supplyAsync (x => getUserInfo (userId)). thenApply (userInfo => getUserRating (userInfo)) hoặc bất cứ thứ gì tương tự, imho dễ đọc hơn và không bắt buộc phải bọc TẤT CẢ các loại trả về vào CompletableFuture
user1694306

@ user1694306 Việc nó có thiết kế kém hay không còn tùy thuộc vào việc xếp hạng của người dùng có trong UserInfo(thì có) hay không hay phải lấy riêng, thậm chí có thể tốn kém (sau đó là không).
glglgl

42

Javadocs được cập nhật trong Java 9 có thể sẽ giúp hiểu rõ hơn về nó:

thenApply

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

Trả về một giá trị mới CompletionStage, khi giai đoạn này hoàn thành bình thường, được thực thi với kết quả của giai đoạn này là đối số của hàm được cung cấp.

Phương pháp này tương tự với Optional.mapStream.map.

Xem CompletionStagetài liệu để biết các quy tắc bao gồm hoàn thành đặc biệt.

thenCompose

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

Trả về CompletionStagegiá trị mới được hoàn thành với cùng giá trị với giá trị được CompletionStagetrả về bởi hàm đã cho.

Khi giai đoạn này hoàn thành bình thường, hàm đã cho được gọi với kết quả của giai đoạn này làm đối số, trả về kết quả khác CompletionStage. Khi giai đoạn đó hoàn thành bình thường, giá trị CompletionStagetrả về của phương thức này được hoàn thành với cùng một giá trị.

Để đảm bảo tiến độ, chức năng được cung cấp phải sắp xếp việc hoàn thành cuối cùng kết quả của nó.

Phương pháp này tương tự với Optional.flatMapStream.flatMap.

Xem CompletionStagetài liệu để biết các quy tắc bao gồm hoàn thành đặc biệt.


14
Tôi tự hỏi tại sao họ không đặt tên cho những chức năng đó mapflatMapngay từ đầu.
Matthias Braun

1
@MatthiasBraun Tôi nghĩ đó là vì thenApply()sẽ gọi đơn giản Function.apply()thenCompose()hơi giống với việc soạn các hàm.
Didier L

14

thenApplythenComposelà các phương pháp của CompletableFuture. Sử dụng chúng khi bạn có ý định làm điều gì đó để có CompleteableFuturekết quả là a Function.

thenApplythenComposecả hai đều trả về a CompletableFuturelà kết quả của riêng chúng. Bạn có thể chuỗi nhiều thenApplyhoặc thenComposecùng nhau. Cung cấp a Functioncho mỗi cuộc gọi, kết quả của nó sẽ là đầu vào cho lệnh tiếp theo Function.

Các Functionbạn cung cấp đôi khi cần phải làm điều gì đó đồng bộ. Loại trả về của bạn Functionphải là Futureloại không phải . Trong trường hợp này bạn nên sử dụng 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

Lần khác, bạn có thể muốn thực hiện xử lý không đồng bộ trong việc này Function. Trong trường hợp đó bạn nên sử dụng thenCompose. Loại trả lại của bạn Functionphải là a CompletionStage. Tiếp theo Functiontrong chuỗi sẽ lấy kết quả của đó CompletionStagelàm đầu vào, do đó mở gói 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

Đây là một ý tưởng tương tự như của Javascript Promise. Promise.thencó thể chấp nhận một hàm trả về một giá trị hoặc Promisemột giá trị. Lý do tại sao hai phương thức này có tên khác nhau trong Java là do tính năng xóa chung . Function<? super T,? extends U> fnFunction<? super T,? extends CompletionStage<U>> fnđược coi là cùng một loại Thời gian chạy - Function. Do đó thenApplythenComposephải được đặt tên rõ ràng, nếu không trình biên dịch Java sẽ phàn nàn về các chữ ký phương thức giống hệt nhau. Kết quả cuối cùng là, Javascript Promise.thenđược triển khai thành hai phần - thenApplythenCompose- trong Java.

Bạn có thể đọc câu trả lời khác của tôi nếu bạn cũng bối rối về một chức năng liên quan thenApplyAsync.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.