ความแตกต่างระหว่างอนาคตและสัญญาคืออะไร?


274

ความแตกต่างระหว่างFutureและPromiseคืออะไร
พวกเขาทั้งสองทำตัวเหมือนเป็นตัวแทนสำหรับผลลัพธ์ในอนาคต แต่ความแตกต่างหลักอยู่ที่ไหน


99
คุณสามารถสร้างPromiseและขึ้นอยู่กับคุณที่จะเก็บมันไว้ เมื่อคนอื่นทำให้คุณสัญญาคุณต้องรอเพื่อดูว่าพวกเขาให้เกียรติในFuture
เควินไรท์


30
หนึ่งในบทความ Wikipedia ที่มีประโยชน์น้อยที่สุดที่ฉันเคยอ่าน
Fulluphigh

คำตอบ:


145

ตามที่การสนทนานี้ , Promiseที่สุดก็ได้รับการเรียกว่าCompletableFutureสำหรับการรวมใน Java 8 และJavadoc มันอธิบายว่า:

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

ตัวอย่างมีให้ในรายการ:

f.then((s -> aStringFunction(s)).thenAsync(s -> ...);

โปรดทราบว่า API ขั้นสุดท้ายนั้นแตกต่างกันเล็กน้อย แต่อนุญาตการดำเนินการแบบอะซิงโครนัสที่คล้ายกัน

CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);

78
ไม่ใช่ความผิดของคุณ Assylias แต่สารสกัด javadoc นั้นจำเป็นต้องมีการปรับปรุงอย่างจริงจังโดยผู้เขียนเทคโนโลยีที่เหมาะสม ในการอ่านครั้งที่ห้าของฉันฉันสามารถเริ่มเห็นคุณค่าในสิ่งที่มันพยายามจะพูด ... และฉันมาที่นี่ด้วยความเข้าใจในอนาคตและสัญญาที่มีอยู่แล้ว!
บีทรูทบีทรูท

2
@ Beetroot-Beetroot ดูเหมือนว่าจะเกิดขึ้นแล้ว
เฮอร์แมน

1
@herman ขอบคุณ - ฉันได้อัปเดตลิงก์ให้ชี้ไปที่ javadoc รุ่นสุดท้ายแล้ว
assylias

7
@ Beetroot-Beetroot คุณควรตรวจสอบเอกสารสำหรับวิธีการพิเศษ มันจะทำให้บทกวีที่ยอดเยี่ยม แต่เป็นความล้มเหลวที่ยอดเยี่ยมของเอกสารที่อ่านได้
Fulluphigh

4
สำหรับทุกคนที่สงสัยว่า @Fulluphigh จะหมายถึงนี้ ดูเหมือนว่าจะถูกลบออก / ซ่อมแซมใน Java 8
Cedric Reichenbach

148

(ฉันไม่พอใจกับคำตอบทั้งหมดดังนั้นนี่คือความพยายามของฉัน ... )

ฉันคิดว่าความคิดเห็นของ Kevin Wright ( "คุณสามารถทำสัญญาและมันก็ขึ้นอยู่กับคุณที่จะเก็บไว้เมื่อมีคนอื่นทำให้คุณสัญญาคุณต้องรอเพื่อดูว่าพวกเขาให้เกียรติในอนาคต" ) สรุปได้ค่อนข้างดี แต่บางคน คำอธิบายมีประโยชน์

ฟิวเจอร์และสัญญาเป็นแนวคิดที่คล้ายกันสวยความแตกต่างคืออนาคตเป็นที่บรรจุแบบอ่านอย่างเดียวสำหรับผลลัพธ์ที่ยังไม่มีในขณะที่สัญญาสามารถเขียนได้ (ปกติเพียงครั้งเดียวเท่านั้น) Java 8 CompletableFutureและ Guava SettableFutureสามารถถูกมองว่าเป็นสัญญาได้เพราะค่าของมันสามารถตั้งค่าได้ ("เสร็จสมบูรณ์") แต่พวกเขายังใช้ส่วนต่อประสานในอนาคต

ผลลัพธ์ของอนาคตจะถูกกำหนดโดย "บุคคลอื่น" - โดยผลลัพธ์ของการคำนวณแบบอะซิงโครนัส โปรดทราบว่าFutureTask - อนาคตคลาสสิก - ต้องเริ่มต้นด้วย Callable หรือ Runnable ไม่มีตัวสร้างแบบไม่มีอาร์กิวเมนต์และทั้ง Future และ FutureTask เป็นแบบอ่านอย่างเดียวจากภายนอก (วิธีการที่ตั้งของ FutureTask ได้รับการป้องกัน) ค่าจะถูกตั้งค่าเป็นผลลัพธ์ของการคำนวณจากภายใน

ในทางกลับกันผลลัพธ์ของคำสัญญาสามารถตั้งค่าได้โดย "คุณ" (หรือในความเป็นจริงโดยใครก็ได้) เพราะมันมีวิธีการตั้งค่าสาธารณะ CompletableFuture และ SettableFuture สามารถสร้างได้โดยไม่ต้องทำงานใด ๆ และสามารถตั้งค่าได้ตลอดเวลา คุณส่งสัญญาไปยังรหัสลูกค้าและปฏิบัติตามในภายหลังตามที่คุณต้องการ

โปรดทราบว่า CompletableFuture ไม่ใช่คำสัญญาที่ "บริสุทธิ์" มันสามารถเริ่มต้นได้ด้วยภารกิจเช่นเดียวกับ FutureTask และคุณลักษณะที่มีประโยชน์ที่สุดคือการผูกมัดขั้นตอนการประมวลผลที่ไม่เกี่ยวข้อง

โปรดทราบว่าสัญญาไม่จำเป็นต้องเป็นประเภทย่อยในอนาคตและไม่จำเป็นต้องเป็นวัตถุเดียวกัน ในกาลาวัตถุในอนาคตจะถูกสร้างขึ้นโดยการคำนวณตรงกันหรือโดยที่แตกต่างกันวัตถุสัญญา ใน C ++ สถานการณ์คล้ายกัน: ผู้ใช้งานจะใช้ออบเจกต์สัญญาและออบเจคในอนาคต ข้อดีของการแยกนี้คือลูกค้าไม่สามารถกำหนดมูลค่าของอนาคตได้

ทั้งSpringและEJB 3.1มีคลาส AsyncResult ซึ่งคล้ายกับสัญญา Scala / C ++ AsyncResult ดำเนินการในอนาคต แต่นี่ไม่ใช่อนาคตที่แท้จริง: วิธีการแบบอะซิงโครนัสใน Spring / EJB จะส่งคืนวัตถุในอนาคตแบบอ่านอย่างเดียวที่แตกต่างกันโดยใช้เวทมนตร์พื้นหลังและไคลเอนต์ในอนาคตที่สอง


116

ฉันทราบว่ามีคำตอบที่ยอมรับแล้ว แต่ต้องการเพิ่มสองเซนต์ของฉัน:

TLDR: อนาคตและสัญญาที่มีทั้งสองด้านของการดำเนินการไม่ตรงกัน: ผู้บริโภค / โทรเทียบกับผู้ผลิต / implementor

ในฐานะผู้เรียกใช้เมธอดอะซิงโครนัส API คุณจะได้รับFutureการจัดการกับผลลัพธ์ของการคำนวณ คุณสามารถโทรหาget()มันเพื่อรอให้การคำนวณเสร็จสมบูรณ์และดึงผลลัพธ์ออกมาได้

ตอนนี้ลองคิดดูว่าวิธีการใช้ API นี้ได้ผลจริง: ผู้ดำเนินการต้องส่งคืนFutureทันที พวกเขามีความรับผิดชอบในการทำให้อนาคตนั้นเสร็จสมบูรณ์ทันทีที่การคำนวณเสร็จสิ้น (ซึ่งพวกเขาจะรู้เพราะกำลังใช้ตรรกะการจัดส่ง ;-)) พวกเขาจะใช้ a Promise/ CompletableFutureเพื่อทำสิ่งนั้นสร้างและคืนCompletableFutureทันทีและเรียกcomplete(T result)เมื่อการคำนวณเสร็จสิ้น


1
สิ่งนี้หมายความว่าสัญญาเป็นประเภทย่อยของอนาคตเสมอและความสามารถในการเขียนในอนาคตนั้นถูกบดบังด้วยประเภทหรือไม่?
devios1

ผมไม่คิดว่ามันส่อให้เห็น การนำไปใช้งานอย่างชาญฉลาดมักจะเป็นเช่นนั้น (เช่นใน Java, Scala)
Rahel Lüthy

74

ฉันจะยกตัวอย่างว่าสัญญาคืออะไรและจะตั้งค่าได้อย่างไรในเวลาใดก็ได้ตรงกันข้ามกับอนาคตซึ่งสามารถอ่านค่าได้เท่านั้น

สมมติว่าคุณมีแม่และขอเงินจากเธอ

// Now , you trick your mom into creating you a promise of eventual
// donation, she gives you that promise object, but she is not really
// in rush to fulfill it yet:
Supplier<Integer> momsPurse = ()-> {

        try {
            Thread.sleep(1000);//mom is busy
        } catch (InterruptedException e) {
            ;
        }

        return 100;

    };


ExecutorService ex = Executors.newFixedThreadPool(10);

CompletableFuture<Integer> promise =  
CompletableFuture.supplyAsync(momsPurse, ex);

// You are happy, you run to thank you your mom:
promise.thenAccept(u->System.out.println("Thank you mom for $" + u ));

// But your father interferes and generally aborts mom's plans and 
// completes the promise (sets its value!) with far lesser contribution,
// as fathers do, very resolutely, while mom is slowly opening her purse 
// (remember the Thread.sleep(...)) :
promise.complete(10); 

ผลลัพธ์ของนั่นคือ:

Thank you mom for $10

คำสัญญาของแม่ถูกสร้างขึ้น แต่รอเหตุการณ์ "เสร็จสมบูรณ์" บางอย่าง

CompletableFuture<Integer> promise...

คุณสร้างกิจกรรมดังกล่าวยอมรับคำสัญญาของเธอและประกาศแผนการของคุณเพื่อขอบคุณแม่ของคุณ:

promise.thenAccept...

ในขณะนี้แม่เริ่มเปิดกระเป๋าเงินของเธอ ... แต่ช้ามาก ...

และพ่อเข้ามาแทรกแซงเร็วกว่ามากและทำตามสัญญาแทนแม่ของคุณ:

promise.complete(10);

คุณสังเกตเห็นผู้บริหารที่ฉันเขียนไว้อย่างชัดเจนหรือไม่?

ที่น่าสนใจถ้าคุณใช้ตัวจัดการปริยายแบบปริยายแทน (commonPool) และพ่อไม่ได้อยู่ที่บ้าน แต่มีเพียงแม่ที่มี "กระเป๋าเงินช้า" ดังนั้นสัญญาของเธอก็จะเสร็จสมบูรณ์หากโปรแกรมมีอายุการใช้งานนานกว่าแม่จะต้องได้รับเงินจาก เงิน

ตัวจัดการเริ่มต้นทำหน้าที่เหมือน "daemon" และไม่รอให้สัญญาทั้งหมดเป็นจริง ฉันไม่พบคำอธิบายที่ดีเกี่ยวกับข้อเท็จจริงนี้ ...


8
มันสนุกมากที่ได้อ่านอันนี้! ฉันไม่คิดว่าฉันจะลืมอนาคตได้และสัญญาอีกต่อไป
user1532146

2
ต้องยอมรับว่าเป็นคำตอบ มันเหมือนกับการอ่านเรื่องราว ขอบคุณ @Vladimir
Phillen

ขอบคุณ @Vladimir
intvprep

9

ไม่แน่ใจว่านี่อาจเป็นคำตอบ แต่เมื่อฉันเห็นสิ่งที่คนอื่นพูดกับใครบางคนมันอาจดูเหมือนว่าคุณต้องการ abstractions ที่แยกจากกันสำหรับทั้งสองแนวคิดเหล่านี้เพื่อให้หนึ่งในนั้น ( Future) เป็นเพียงมุมมองแบบอ่านอย่างเดียวPromise) ... แต่จริงๆแล้วสิ่งนี้ไม่จำเป็น

ตัวอย่างเช่นดูที่สัญญาที่กำหนดไว้ในจาวาสคริปต์:

https://promisesaplus.com/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

มุ่งเน้นไปที่การจัดองค์ประกอบที่ใช้thenวิธีการเช่น:

asyncOp1()
.then(function(op1Result){
  // do something
  return asyncOp2();
})
.then(function(op2Result){
  // do something more
  return asyncOp3();
})
.then(function(op3Result){
  // do something even more
  return syncOp4(op3Result);
})
...
.then(function(result){
  console.log(result);
})
.catch(function(error){
  console.log(error);
})

ซึ่งทำให้การคำนวณแบบอะซิงโครนัสมีลักษณะคล้ายกัน:

try {
  op1Result = syncOp1();
  // do something
  op1Result = syncOp2();
  // do something more
  op3Result = syncOp3();
  // do something even more
  syncOp4(op3Result);
  ...
  console.log(result);
} catch(error) {
  console.log(error);
}

ซึ่งค่อนข้างเท่ห์ (ไม่เจ๋งเท่าasync-awaitแต่async-awaitเพียงลบ boilerplate .... จากนั้น (ฟังก์ชั่น (ผลลัพธ์) {....จากมัน)

และที่จริงแล้วสิ่งที่เป็นนามธรรมของพวกเขานั้นค่อนข้างดีในฐานะผู้สร้างสัญญา

new Promise( function(resolve, reject) { /* do it */ } );

ช่วยให้คุณสามารถโทรกลับสองครั้งซึ่งสามารถใช้เพื่อดำเนินการให้Promiseสำเร็จหรือมีข้อผิดพลาด ดังนั้นเฉพาะรหัสที่สร้างPromiseสามารถกรอกได้และรหัสที่ได้รับPromiseวัตถุที่สร้างไว้แล้วมีมุมมองแบบอ่านอย่างเดียว

ด้วยการรับมรดกสามารถทำได้หากการแก้ไขและการปฏิเสธเป็นวิธีการป้องกัน


4
+1 นี่คือคำตอบที่ถูกต้องสำหรับคำถามนี้ CompletableFutureอาจมีความคล้ายคลึงกันกับ a Promiseแต่ก็ยังไม่ใช่Promiseเพราะวิธีที่ตั้งใจจะใช้จะแตกต่างกัน: Promiseผลลัพธ์จะถูกใช้โดยการโทรthen(function)และฟังก์ชันจะถูกดำเนินการในบริบทของผู้ผลิตทันทีหลังจากที่ผู้ผลิตเรียกresolve. Future's ผลมีการบริโภคโดยการโทรgetซึ่งเป็นสาเหตุของด้ายผู้บริโภคที่จะรอจนกว่าด้ายผลิตได้สร้างมูลค่าแล้วประมวลผลในผู้บริโภค Futureเป็นแบบมัลติเธรดโดยเนื้อแท้ แต่ ...
Periata Breatta

5
... เป็นไปได้ทั้งหมดที่จะใช้Promiseเพียงเธรดเดียว (และในความเป็นจริงนั่นคือสภาพแวดล้อมที่แม่นยำซึ่งเดิมถูกออกแบบมาสำหรับ: แอปพลิเคชันจาวาสคริปต์โดยทั่วไปจะมีเธรดเดียวเท่านั้นดังนั้นคุณจึงไม่สามารถติดตั้งได้Future ) Promiseดังนั้นจึงมีน้ำหนักเบาและมีประสิทธิภาพมากกว่าFutureแต่Futureจะมีประโยชน์ในสถานการณ์ที่ซับซ้อนและต้องการความร่วมมือระหว่างเธรดที่ไม่สามารถจัดเรียงได้ง่ายโดยใช้Promises เพื่อสรุป: Promiseเป็นแบบจำลองการผลักดันในขณะที่Futureเป็นแบบจำลองการดึง (cf Iterable vs Observable)
Periata Breatta

@PeriataBreatta แม้จะอยู่ในสภาพแวดล้อมแบบเธรดเดียวก็ต้องมีบางสิ่งที่ทำให้สัญญาสำเร็จ (ซึ่งโดยทั่วไปแล้วการเรียงลำดับของการทำงานเป็นเธรดอื่นเช่น an XMLHttpRequest) ฉันไม่เชื่อว่าการอ้างสิทธิ์อย่างมีประสิทธิภาพคุณมีตัวเลขบ้างไหม? +++ นั่นคือคำอธิบายที่ดีมาก
maaartinus

1
@ maaartinus - ใช่สิ่งที่ต้องทำตามสัญญา แต่สามารถทำได้ (และในความเป็นจริงในหลาย ๆ กรณี) ทำได้โดยใช้ลูประดับบนสุดที่โพลสำหรับการเปลี่ยนแปลงในสถานะภายนอกและแก้ไขข้อสัญญาที่เกี่ยวข้องกับการกระทำที่เสร็จสิ้น ประสิทธิภาพฉลาดผมไม่มีตัวเลขที่มั่นคงสำหรับสัญญาเฉพาะ แต่ทราบว่าการโทรgetบนได้รับการแก้ไขFutureจะจำเป็นต้องเกี่ยวข้องกับหัวข้อที่ 2 สวิทช์บริบทซึ่งอย่างน้อยไม่กี่ปีที่ผ่านมาก็น่าจะเป็นที่จะต้องใช้ประมาณ 50 เรา
Periata Breatta

@PeriataBreatta ความคิดเห็นของคุณควรเป็นทางออกที่ได้รับการยอมรับ ฉันกำลังมองหาคำอธิบาย (pull / push, single / multi-thread) อย่างคุณ
Thomas Jacob

5

สำหรับรหัสลูกค้าสัญญาคือการสังเกตหรือแนบโทรกลับเมื่อผลลัพธ์ที่มีอยู่ในขณะที่ในอนาคตคือการรอผลแล้วดำเนินการต่อ ในทางทฤษฎีอะไรก็ตามที่เป็นไปได้ที่จะทำกับฟิวเจอร์สสิ่งที่สามารถทำได้กับสัญญา แต่เนื่องจากความแตกต่างของรูปแบบ API ผลลัพธ์สำหรับสัญญาในภาษาที่แตกต่างกันทำให้การผูกมัดง่ายขึ้น


2

ไม่มีวิธีการตั้งค่าในอินเทอร์เฟซในอนาคตเพียงรับเมธอดเท่านั้นดังนั้นจึงเป็นแบบอ่านอย่างเดียว เกี่ยวกับเสร็จสมบูรณ์ในอนาคตบทความนี้อาจมีประโยชน์ completablefuture

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