ข้อแตกต่างระหว่าง Deferred, Promise และ Future ใน JavaScript คืออะไร


300

อะไรคือความแตกต่างระหว่างรอการตัดบัญชีสัญญาและสัญญาซื้อขายล่วงหน้า?
มีทฤษฎีที่ได้รับการรับรองโดยทั่วไปอยู่เบื้องหลังทั้งสามนี้หรือไม่?


11
ฉันไม่คิดว่ามีอะไรจะทำอย่างไรกับ jQuery ...
BoltClock

11
ควรอ่านสิ่งนี้: msdn.microsoft.com/en-us/scriptjunkie/gg723713
jfriend00

8
ฉันไม่ได้ใช้พวกเขาเอง แต่นี่คือบทนำที่ดีงามในวิกิพีเดียen.wikipedia.org/wiki/Futures_and_promises แม้ว่าฉันจะไม่เข้าใจกรณีการใช้อย่างถูกต้อง ในภาษาที่ขับเคลื่อนด้วยเหตุการณ์ async เช่น javascript เมื่อเห็นอย่างรวดเร็วครั้งแรกฉันไม่เห็นสิ่งที่พวกเขาเสนอให้ในการโทรกลับนอกเหนือจากอาจเป็น api ที่สะอาดกว่า ฉันจะรักมันถ้ามีคนให้ตัวอย่างกรณีการใช้งานและแสดงให้เห็นว่าแนวคิดเหล่านี้ถูกนำไปใช้อย่างไรและเหตุใดการโทรกลับจึงเป็นโซลูชันที่ไม่มีประสิทธิภาพ @duri สิ่งนี้ไม่เกี่ยวข้องกับ jQuery สามารถลบแท็ก jQuery ได้
ไหม

2
@ ลิงก์ที่ยอดเยี่ยม jfriend00 น่าจะเป็นคำตอบที่ดี
fncomp

@ jfriend00 ลิงค์ใหม่ - msdn.microsoft.com/en-us/magazine/gg723713.aspx
c69

คำตอบ:


97

ในแง่ของความไม่ชัดเจนว่าฉันพยายามตอบคำถามของ OP อย่างไร คำตอบที่แท้จริงคือสัญญาเป็นสิ่งที่ใช้ร่วมกันกับวัตถุอื่น ๆ ในขณะที่รอการตัดบัญชีควรจะเป็นส่วนตัว ในขั้นต้นการรอการตัดบัญชี (ซึ่งโดยทั่วไปจะขยายสัญญา) สามารถแก้ไขตัวเองในขณะที่สัญญาอาจไม่สามารถทำได้

หากคุณกำลังสนใจในข้อปลีกย่อยแล้วตรวจสอบสัญญา / A +


เท่าที่ฉันทราบวัตถุประสงค์ที่ครอบคลุมคือการปรับปรุงความชัดเจนและลดการเชื่อมต่อผ่านส่วนต่อประสานมาตรฐาน ดูการอ่านที่แนะนำจาก @ jfriend00:

แทนที่จะส่งผ่านการเรียกกลับไปยังฟังก์ชันโดยตรงสิ่งที่สามารถนำไปสู่อินเทอร์เฟซที่เชื่อมโยงกันอย่างแน่นหนาการใช้สัญญาอนุญาตให้แยกข้อกังวลสำหรับโค้ดที่ซิงโครนัสหรืออะซิงโครนัส

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

แน่นอนเปรียบเทียบรูปแบบการโทรกลับบริสุทธิ์ของการทำอะไรบางอย่างหลังจากโหลด CodeMirror ในโหมด JS แบบอะซิงโครนัส (ขอโทษฉันไม่ได้ใช้ jQuery ในขณะที่ ):

/* assume getScript has signature like: function (path, callback, context) 
   and listens to onload && onreadystatechange */
$(function () {
   getScript('path/to/CodeMirror', getJSMode);

   // onreadystate is not reliable for callback args.
   function getJSMode() {
       getScript('path/to/CodeMirror/mode/javascript/javascript.js', 
           ourAwesomeScript);
   };

   function ourAwesomeScript() {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   };
});

สำหรับเวอร์ชันของสูตรสัญญา (อีกครั้งขอโทษฉันยังไม่เข้าใจ jQuery):

/* Assume getScript returns a promise object */
$(function () {
   $.when(
       getScript('path/to/CodeMirror'),
       getScript('path/to/CodeMirror/mode/javascript/javascript.js')
   ).then(function () {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   });
});

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


10
แม้ว่าคำตอบนี้อาจมีประโยชน์ แต่ก็ไม่ได้ตอบคำถามที่จริงแล้วสิ่งที่เรียกว่าการรอการตัดบัญชีเป็นอนาคตหรือสัญญาขึ้นอยู่กับการดำเนินการ
awdz9nld

@ MartinKällmanคุณพูดถูก! ฉันไม่ได้มาที่นี่อีกซักพักแล้วและได้เรียนรู้เล็กน้อย ฉันจะโพสต์คำตอบแยกต่างหากด้านล่าง แต่ปล่อยให้เรื่องนี้เพราะคนดูเหมือนจะได้รับประโยชน์จากตัวอย่างการใช้งาน
fncomp

@ MartinKällmanพิจารณาเขียนคำตอบใหม่ อย่างไรก็ตามฉันคิดว่า OP ต้องการจะรู้ว่าสัญญาและรอการตัดบัญชีมีไว้เพื่ออะไร คำตอบสำหรับคำถามที่เกิดขึ้นจริงของเขาจะประมาณ "deferreds สามารถแก้ปัญหาของพวกเขาด้วยตนเอง AFAIK ทฤษฎีที่อยู่เบื้องหลังสัญญาและ deferreds มาจาก [หน้าที่ Programming ปฏิกิริยา. | haskell.org/haskellwiki/Functional_Reactive_Programming]ซึ่งเป็นเทคนิคในการเรียกกลับแฟบ ."
fncomp

2
นี่เป็นเพียงความผิดทั้งหมดและตัวอย่างของคุณนั้นง่ายสำหรับการโทรกลับ คำสัญญาไม่ได้เกี่ยวกับการรวมการโทรกลับและการแยกการเสนอ แต่ให้ DSL เพื่อเขียนรหัส async เช่นการเขียนรหัสการซิงค์ โดยเฉพาะอย่างยิ่งfn(callback, errback)ไม่คู่ใด ๆ ที่แน่นหรือมีประโยชน์น้อยกว่าfn().then(callback, errback)- แต่นั่นเป็นวิธีที่ผิดที่จะใช้สัญญาอยู่แล้ว ฉันเกลียด$.whenตัวอย่างลัทธิขนส่งสินค้าโดยเฉพาะ- ไม่มีเหตุผลใดที่คุณไม่มี$.whenฟังก์ชั่นที่ทำงานกับการโทรกลับ
Esailija

นี่ไม่ได้ตอบคำถามแม้ว่า +1 ที่ฉันสามารถรู้ว่านรกโทรกลับคืออะไร
Bhojendra Rauniyar

146

คำตอบเหล่านี้รวมถึงคำตอบที่เลือกเป็นสิ่งที่ดีสำหรับการแนะนำแนวคิดสัญญา แต่ขาดในรายละเอียดของสิ่งที่แตกต่างในคำศัพท์ที่เกิดขึ้นเมื่อมีการใช้ห้องสมุดการใช้พวกเขา (และมี มีความแตกต่างที่สำคัญ)

เนื่องจากมันยังคงเป็นข้อมูลจำเพาะที่มีการพัฒนาอยู่ในขณะนี้คำตอบมาจากการพยายามสำรวจทั้งการอ้างอิง (เช่นวิกิพีเดีย ) และการใช้งาน (เช่นjQuery ):

  • รอการตัดบัญชี : ไม่เคยอธิบายไว้ในข้อมูลอ้างอิงที่เป็นที่นิยม 1 2 3 4 แต่โดยทั่วไปจะใช้งานโดยการใช้งานในฐานะผู้ตัดสินชี้ขาดสัญญา (การนำไปปฏิบัติและ) 5 6 7 resolvereject

    บางครั้ง deferreds นอกจากนี้ยังมีสัญญา (การดำเนินการthen) 5 6 เวลาอื่น ๆ ก็เห็นเป็นที่บริสุทธิ์มากขึ้นที่จะมีการตัดบัญชีเพียงความสามารถในความละเอียดและบังคับให้ผู้ใช้สามารถเข้าถึงสัญญาสำหรับการใช้ 7 then

  • สัญญา : คำที่ซับซ้อนที่สุดสำหรับกลยุทธ์ภายใต้การสนทนา

    วัตถุพร็อกซีที่เก็บผลลัพธ์ของฟังก์ชันเป้าหมายที่มีความสอดคล้องกันที่เราต้องการสรุปรวมถึงการเปิดเผยthenฟังก์ชั่นที่รับฟังก์ชั่นเป้าหมายอื่นและส่งคืนสัญญาใหม่ 2

    ตัวอย่างจากCommonJS :

    > asyncComputeTheAnswerToEverything()
        .then(addTwo)
        .then(printResult);
    44

     

    อธิบายเสมอในการอ้างอิงที่เป็นที่นิยมถึงแม้ว่าจะไม่ได้ระบุว่าการแก้ปัญหาความรับผิดชอบตกไปอยู่ที่ใด 1 2 3 4

    นำเสนอในการใช้งานที่เป็นที่นิยมเสมอ 5 6 7

  • ในอนาคต : คำที่ดูเหมือนจะคัดค้านพบในการอ้างอิงที่ได้รับความนิยม 1 และอย่างน้อยหนึ่งการใช้งานที่เป็นที่นิยม [ 8] แต่ดูเหมือนจะไม่ได้พูดคุยกันในการตั้งค่าสำหรับคำว่า 'สัญญา' 3 และไม่ได้กล่าวถึงเสมอ 9

    อย่างไรก็ตามมีอย่างน้อยหนึ่งไลบรารีที่ใช้คำทั่วไปสำหรับการทำให้เกิดความสอดคล้องกันทางนามธรรมและการจัดการข้อผิดพลาดในขณะที่ไม่ได้จัดเตรียมการthenทำงาน 10 ไม่ชัดเจนหากหลีกเลี่ยงคำว่า 'สัญญา' โดยเจตนา แต่อาจเป็นทางเลือกที่ดีเนื่องจากมีการสร้างสัญญาขึ้นใน 2

อ้างอิง

  1. Wikipedia เกี่ยวกับสัญญาและอนาคต
  2. สัญญา / A + ข้อมูลจำเพาะ
  3. มาตรฐาน DOM เกี่ยวกับสัญญา
  4. ข้อมูลจำเพาะสัญญามาตรฐานของ WIP ของ DOM
  5. ชุดเครื่องมือ DOJO รอการตัดบัญชี
  6. jQuery Deferreds
  7. Q
  8. FutureJS
  9. ส่วน Javascript ที่ใช้งานได้ในสัญญา
  10. ฟิวเจอร์ในการทดสอบการรวม AngularJS

อื่น ๆ ที่อาจทำให้สับสน


5
หากต้องการเพิ่มความกระจ่างเพิ่มเติมเกี่ยวกับคำว่า "อนาคต" - ฟิวเจอร์สมีประวัติอันยาวนานในภาษาการเขียนโปรแกรมจำนวนมากซึ่งย้อนหลังไปถึงช่วงกลางทศวรรษที่ 80 และคำนี้ยังใช้กันอย่างแพร่หลายในปัจจุบันโดยเฉพาะใน JVM ดูเหมือนว่า JavaScript ได้เลือกใช้คำว่า "Promise" เพื่อหมายถึงบางสิ่งที่คล้ายคลึงกับ Java ในอนาคต Scala แยกแนวคิดเดียวกันออกเป็น "อนาคต" และ "สัญญา" เพื่ออ้างอิงถึงหมายเลขอ้างอิง "อ่าน" และหมายเลขอ้างอิง "เขียน" ของสิ่งที่โปรแกรมเมอร์ JavaScript เรียกว่าสัญญา
Heather Miller

1
และแน่นอนว่า Microsoft ต้องคิดคำของตัวเองขึ้นมาดังนั้นใน C # พวกเขาถูกเรียกว่าTask
BlueRaja - Danny Pflughoeft

72

สิ่งที่ทำให้ทุกอย่างคลิกสำหรับฉันคือการนำเสนอโดยโดเมนิคเดนิโคลา

ในส่วนแก็บ Githubเขาให้คำอธิบายที่ฉันชอบที่สุดมันกระชับมาก:

จุดประสงค์ของสัญญาคือการให้องค์ประกอบการทำงานและข้อผิดพลาดกลับมาในโลกของ async

ในคำอื่น ๆ สัญญาเป็นวิธีที่ช่วยให้เราเขียนโค้ดแบบอะซิงโครนัสที่เกือบจะง่ายต่อการเขียนราวกับว่ามันเป็นแบบซิงโครนั

ลองพิจารณาตัวอย่างนี้โดยมีสัญญา:

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

มันทำงานเหมือนกับว่าคุณกำลังเขียนรหัสซิงโครนัสนี้:

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

(ถ้ายังฟังดูซับซ้อนอยู่ให้ดูงานนำเสนอนั้น!)

เกี่ยวกับรอการตัดบัญชีมันเป็นวิธีการ.resolve()หรือ.reject()สัญญา ในข้อมูลจำเพาะสัญญา / Bจะถูกเรียก.defer()ใช้ ใน jQuery $.Deferred()ก็

โปรดทราบว่าเท่าที่ฉันทราบการดำเนินการตามสัญญาใน jQuery นั้นเสีย (ดูส่วนสำคัญ) อย่างน้อยเท่ากับ jQuery 1.8.2
มันควรจะใช้Promises / Aแล้ว แต่คุณไม่ได้รับข้อผิดพลาดที่ถูกต้องในการจัดการในแง่ที่ว่าฟังก์ชั่น "async try / catch" ทั้งหมดไม่ทำงาน อันไหนน่าเสียดายเพราะการมี "ลอง / จับ" ด้วยรหัส async นั้นยอดเยี่ยมมาก

หากคุณกำลังจะใช้สัญญา (คุณควรพยายามที่พวกเขาออกมาพร้อมกับรหัสของคุณเอง!) ให้ใช้กริช Kowal ของ Q รุ่น jQuery เป็นเพียงตัวรวบรวมการเรียกกลับบางส่วนสำหรับการเขียนโค้ด jQuery ที่สะอาดขึ้น แต่พลาดจุดไป

เกี่ยวกับอนาคตฉันไม่มีความคิดฉันไม่เห็นว่าใน API ใด ๆ

แก้ไข: youtube พูดคุยเกี่ยวกับสัญญาจากDomenic Denicolaจากความคิดเห็นของ@Farmด้านล่าง

ข้อความจาก Michael Jackson (ใช่Michael Jackson ) จากวิดีโอ:

ฉันต้องการให้คุณเผาผลาญวลีนี้ในใจของคุณ: สัญญาเป็นมูลค่าไม่ตรงกัน

นี่คือคำอธิบายที่ยอดเยี่ยม: สัญญาเป็นเหมือนตัวแปรจากอนาคต - การอ้างอิงชั้นหนึ่งถึงบางสิ่งบางอย่างที่จะมีอยู่ (หรือเกิดขึ้น)


5
คำอธิบายที่ดีของ Futures (ตอนนี้ใช้งานใน DOM!) โดยสมาชิกของทีมหลัก W3 และ Chrome จะพบได้ที่นี่: xanthir.com/b4PY0
oligofren

1
@ oligofren ขอบคุณสำหรับลิงค์ที่ดูเหมือนว่าดี! โดยวิธีการที่สิ่งที่ favicon น่ารำคาญอย่างน่าหัวเราะฮ่า ๆ
Camilo Martin

1
คำตอบนี้ต้องการ upvotes มากขึ้น ควรได้รับคะแนนสูงกว่า IMO คำตอบที่ยอมรับ
Chev

1
youtube ของ Domenic Denicola พูดคุยเรื่องคำสัญญา: youtube.com/watch?v=hf1T_AONQJU
ฟาร์ม

@ ยอดเยี่ยมมาก! ฉันจะเพิ่มเข้าไปในคำตอบ
Camilo Martin

32

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

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

deferred.promise()วิธีการช่วยให้การทำงานไม่ตรงกันเพื่อป้องกันไม่ให้รหัสอื่น ๆ จากการรบกวนความคืบหน้าหรือสถานะของคำขอภายใน สัญญาจะเปิดเผยเฉพาะวิธีการรอการตัดบัญชีที่จำเป็นในการแนบตัวจัดการเพิ่มเติมหรือกำหนดสถานะ ( จากนั้นทำ, ล้มเหลว, เสมอ, ไพพ์, ความคืบหน้า, สถานะและสัญญา ) แต่ไม่ใช่วิธีที่เปลี่ยนสถานะ ( แก้ไขปฏิเสธปฏิเสธแจ้งเตือนแก้ไขด้วย) ปฏิเสธด้วยและแจ้งเตือนด้วย )

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

หากคุณกำลังสร้าง Deferred ให้อ้างอิงถึง Deferred เพื่อให้สามารถแก้ไขหรือปฏิเสธได้ในบางจุด ส่งคืนเฉพาะออบเจกต์ Promise ผ่าน deferred.promise () เพื่อให้โค้ดอื่น ๆ สามารถลงทะเบียนการเรียกกลับหรือตรวจสอบสถานะปัจจุบัน

เพียงแค่เราสามารถพูดได้ว่าสัญญาหมายถึงคุณค่าที่ยังไม่เป็นที่รู้จักในฐานะที่เป็นผู้รอการตัดบัญชีแทนงานที่ยังไม่เสร็จ


ป้อนคำอธิบายรูปภาพที่นี่


1
บวก 1 สำหรับการแสดงแผนภูมิ Bravisimo !! ^ _ ^
Ashok MA

23
  • A promiseแทนค่าที่ยังไม่ทราบ
  • A deferredหมายถึงงานที่ยังไม่เสร็จ

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

การอ้างอิง

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