CompletableFuture | จากนั้นใช้เทียบกับแล้ว


119

ฉันไม่สามารถเข้าใจความแตกต่างระหว่างthenApply() และthenCompose().

แล้วใครช่วยให้กรณีการใช้งานที่ถูกต้อง?

จากเอกสาร Java:

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

ส่งคืนค่าใหม่CompletionStageที่เมื่อขั้นตอนนี้เสร็จสิ้นตามปกติจะถูกดำเนินการด้วยผลลัพธ์ของสเตจนี้เป็นอาร์กิวเมนต์ของฟังก์ชันที่ให้มา

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

ส่งคืนค่าใหม่CompletionStageที่เมื่อขั้นตอนนี้เสร็จสิ้นตามปกติจะถูกดำเนินการด้วยสเตจนี้เป็นอาร์กิวเมนต์ของฟังก์ชันที่ให้มา

ฉันเข้าใจว่าอาร์กิวเมนต์ที่ 2 ของการthenComposeขยาย CompletionStage โดยที่thenApplyไม่ได้

มีใครช่วยยกตัวอย่างได้ไหมว่าฉันต้องใช้ในกรณีใดthenApplyและเมื่อthenComposeใด


39
คุณเข้าใจความแตกต่างระหว่างmapและflatMapในStreamหรือไม่? thenApplyเป็นmapและthenComposeเป็นของflatMap CompletableFutureคุณใช้thenComposeเพื่อหลีกเลี่ยงการมีCompletableFuture<CompletableFuture<..>>.
มิชา

2
@ มิชาขอบคุณสำหรับความคิดเห็นของคุณ ใช่ฉันรู้ความแตกต่างระหว่างmapและflatMapและฉันเข้าใจตรงกัน ขอบคุณอีกครั้ง :)
GuyT

นี่เป็นคำแนะนำที่ดีมากในการเริ่มต้นด้วย CompletableFuture - baeldung.com/java-completablefuture
thealchemist

คำตอบ:


168

thenApply จะใช้ถ้าคุณมีฟังก์ชันการทำแผนที่แบบซิงโครนัส

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

thenComposeจะใช้ถ้าคุณมีฟังก์ชันการทำแผนที่แบบอะซิงโครนัส (เช่นฟังก์ชันที่ส่งกลับ a CompletableFuture) จากนั้นจะส่งคืนอนาคตพร้อมผลลัพธ์โดยตรงแทนที่จะเป็นอนาคตที่ซ้อนกัน

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

14
ทำไมโปรแกรมเมอร์จึงควรใช้.thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))แทน.thenApplyAsync(x -> x+1)? การซิงโครนัสหรืออะซิงโครนัสไม่ใช่ความแตกต่างที่เกี่ยวข้อง
Holger

17
พวกเขาจะไม่ทำเช่นนั้น อย่างไรก็ตามหากไลบรารีของบุคคลที่สามที่พวกเขาใช้ส่งคืน a CompletableFutureนี่จะเป็นthenComposeวิธีที่จะทำให้โครงสร้างแบนราบ
โจค

1
@ArunavSanyal การโหวตแสดงภาพที่แตกต่างออกไป คำตอบนี้ชัดเจนและกระชับ
Alex Shesterov

@Holger อ่านคำตอบอื่นของฉันถ้าคุณสับสนthenApplyAsyncเพราะไม่ใช่อย่างที่คุณคิด
1283822

@ 1283822 ฉันไม่รู้ว่าอะไรทำให้คุณคิดว่าฉันสับสนและไม่มีอะไรในคำตอบของคุณที่สนับสนุนการอ้างว่า“ ไม่ใช่อย่างที่คุณคิด”
โฮลเกอร์

49

ฉันคิดว่าคำตอบที่โพสต์โดย @Joe C ทำให้เข้าใจผิด

ให้ฉันพยายามอธิบายความแตกต่างระหว่างthenApplyและthenComposeด้วยตัวอย่าง

สมมติว่าเรามี 2 วิธี: getUserInfo(int userId)และgetUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

CompletableFutureทั้งสองประเภทมีวิธีการกลับมา

เราต้องการโทรgetUserInfo()ก่อนและเมื่อเสร็จสิ้นให้โทรหาgetUserRating()ผลลัพธ์UserInfoกับที่เกิดขึ้น

เมื่อเสร็จสิ้นของgetUserInfo()วิธีการลองทั้งสองและthenApply thenComposeความแตกต่างอยู่ในประเภทผลตอบแทน:

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

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

thenCompose()ทำงานเหมือนของ ScalaflatMapซึ่งแบนฟิวเจอร์สที่ซ้อนกัน

thenApply()ส่งคืนฟิวเจอร์สที่ซ้อนกันเหมือนเดิม แต่thenCompose()ทำให้แบนที่ซ้อนกันCompletableFuturesเพื่อให้ง่ายต่อการเชื่อมโยงการเรียกวิธีการมากขึ้น


2
แล้วคำตอบของ Joe C ก็ไม่ทำให้เข้าใจผิด เป็นไปอย่างถูกต้องและรัดกุมมากขึ้น
koleS

2
สิ่งนี้มีประโยชน์มาก :-)
dstibbe

1
Imho เป็นการออกแบบที่ไม่ดีในการเขียน CompletableFuture <UserInfo> getUserInfo และ CompletableFuture <UserRating> getUserRating (UserInfo) \\ แทนควรเป็น UserInfo getUserInfo () และ int getUserRating (UserInfo) ถ้าฉันต้องการใช้ async และ chain จากนั้นฉันก็ทำได้ ใช้ ompletableFuture.supplyAsync (x => getUserInfo (userId)) แล้วใช้ (userInfo => getUserRating (userInfo)) หรืออะไรทำนองนี้มันเป็น imho ที่อ่านได้ง่ายขึ้นและไม่บังคับให้รวมประเภทการส่งคืนทั้งหมดลงใน CompletableFuture
user1694306

@ user1694306 ไม่ว่าจะเป็นการออกแบบที่ไม่ดีหรือไม่ขึ้นอยู่กับว่าคะแนนของผู้ใช้มีอยู่ในUserInfo(แล้วใช่) หรือไม่หรือต้องได้รับแยกต่างหากซึ่งอาจมีค่าใช้จ่ายสูง (จากนั้นไม่ใช่)
glglgl

42

Javadocs ที่อัปเดตใน Java 9 อาจช่วยให้เข้าใจได้ดีขึ้น:

thenApply

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

ส่งคืนค่าใหม่CompletionStageที่เมื่อขั้นตอนนี้เสร็จสิ้นตามปกติจะถูกดำเนินการด้วยผลลัพธ์ของสเตจนี้เป็นอาร์กิวเมนต์ของฟังก์ชันที่ให้มา

วิธีการนี้จะคล้ายคลึงกับและOptional.mapStream.map

ดูCompletionStageเอกสารสำหรับกฎที่ครอบคลุมความสมบูรณ์พิเศษ

thenCompose

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

ส่งคืนใหม่CompletionStageที่เสร็จสมบูรณ์ด้วยค่าเดียวกับที่CompletionStageส่งคืนโดยฟังก์ชันที่กำหนด

เมื่อขั้นตอนนี้เสร็จสิ้นตามปกติฟังก์ชันที่กำหนดจะถูกเรียกใช้ด้วยผลลัพธ์ของสเตจนี้เป็นอาร์กิวเมนต์โดยส่งกลับอีก CompletionStageรายการ เมื่อขั้นตอนนั้นเสร็จสิ้นตามปกติการ CompletionStageส่งคืนโดยวิธีนี้จะเสร็จสมบูรณ์ด้วยค่าเดียวกัน

เพื่อให้แน่ใจว่ามีความคืบหน้าฟังก์ชันที่ให้มาจะต้องจัดเตรียมผลลัพธ์ที่เสร็จสมบูรณ์ในที่สุด

วิธีการนี้จะคล้ายคลึงกับและOptional.flatMap Stream.flatMap

ดูCompletionStageเอกสารสำหรับกฎที่ครอบคลุมความสมบูรณ์พิเศษ


14
ผมสงสัยว่าทำไมพวกเขาไม่ได้ชื่อฟังก์ชันเหล่านั้นmapและflatMapในสถานที่แรก
Matthias Braun

1
@MatthiasBraun ฉันคิดว่านั่นเป็นเพราะthenApply()จะเรียกง่ายๆFunction.apply()และthenCompose()คล้ายกับการเขียนฟังก์ชันเล็กน้อย
Didier L

14

thenApplyและมีวิธีการของthenCompose CompletableFutureใช้เมื่อคุณตั้งใจจะทำบางสิ่งบางอย่างเพื่อให้CompleteableFutureได้ผลลัพธ์ด้วยไฟล์Function.

thenApplyและthenComposeทั้งสองส่งกลับCompletableFutureเป็นผลลัพธ์ของตัวเอง คุณสามารถเชื่อมโยงหลายรายการthenApplyหรือthenComposeรวมกัน จัดหาเพื่อการโทรแต่ละครั้งซึ่งผลจะมีการป้อนข้อมูลต่อไปFunctionFunction

สิ่งที่Functionคุณให้มาบางครั้งจำเป็นต้องทำบางอย่างพร้อมกัน ประเภทผลตอบแทนของคุณFunctionควรเป็นFutureประเภทที่ไม่ใช่ ในกรณีนี้คุณควรใช้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

ครั้งอื่น ๆ Functionที่คุณอาจต้องการที่จะทำการประมวลผลไม่ตรงกันในเรื่องนี้ ในกรณีนั้นคุณควรใช้thenCompose. ประเภทการกลับมาของคุณควรจะเป็นFunction CompletionStageถัดไปFunctionในห่วงโซ่จะได้รับผลลัพธ์ของสิ่งนั้นCompletionStageเป็นอินพุตดังนั้นจึงคลายไฟล์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

Promiseนี้เป็นความคิดที่คล้ายกับจาวาสคริปต์ของ Promise.thenสามารถยอมรับฟังก์ชันที่ส่งคืนค่าหรือPromiseค่า เหตุผลที่ว่าทำไมทั้งสองวิธีมีชื่อที่แตกต่างกันในชวาเกิดจากการลบออกทั่วไป Function<? super T,? extends U> fnและFunction<? super T,? extends CompletionStage<U>> fnได้รับการพิจารณาประเภทเดียวกัน Runtime Function- ดังนั้นthenApplyและthenComposeต้องตั้งชื่อให้ชัดเจนมิฉะนั้นคอมไพเลอร์ Java จะบ่นเกี่ยวกับลายเซ็นวิธีการที่เหมือนกัน ผลลัพธ์สุดท้ายคือ Javascript Promise.thenถูกนำไปใช้ในสองส่วน - thenApplyและthenCompose- ใน Java

คุณสามารถอ่านคำตอบอื่น ๆ ของฉันthenApplyAsyncถ้าคุณยังสับสนเกี่ยวกับฟังก์ชั่นที่เกี่ยวข้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.