ฉันควรใช้เมธอด“ then” ของ jQuery deferred เมื่อไรและควรใช้เมธอด“ pipe” เมื่อใด


97

jQuery Deferredมีสองฟังก์ชั่นที่สามารถใช้เพื่อปรับใช้ฟังก์ชัน Chaining แบบอะซิงโครนัส:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacksฟังก์ชันหรืออาร์เรย์ของฟังก์ชันที่เรียกเมื่อ Deferred ได้รับการแก้ไข
failCallbacksฟังก์ชันหรืออาร์เรย์ของฟังก์ชันที่เรียกว่าเมื่อ Deferred ถูกปฏิเสธ

pipe()

deferred.pipe( [doneFilter] [, failFilter] ) Returns: Promise

doneFilterฟังก์ชันทางเลือกที่ถูกเรียกใช้เมื่อ Deferred ได้รับการแก้ไข
failFilterฟังก์ชันทางเลือกที่ถูกเรียกใช้เมื่อ Deferred ถูกปฏิเสธ

ฉันรู้ว่าthen()ใช้เวลานานกว่าเล็กน้อยpipe()ดังนั้นอย่างหลังจึงต้องเพิ่มผลประโยชน์พิเศษบางอย่าง แต่สิ่งที่แตกต่างอย่างชัดเจนก็ทำให้ฉันหายไป ทั้งสองใช้พารามิเตอร์การเรียกกลับที่เหมือนกันแม้ว่าจะแตกต่างกันในชื่อและความแตกต่างระหว่างการส่งคืน a Deferredและส่งคืนPromiseดูเหมือนเล็กน้อย

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

ดังนั้นควรใช้thenเมื่อใดและควรใช้เมื่อpipeใด


ส่วนที่เพิ่มเข้าไป

คำตอบที่ยอดเยี่ยมของเฟลิกซ์ช่วยชี้แจงว่าฟังก์ชันทั้งสองนี้แตกต่างกันอย่างไร แต่ฉันสงสัยว่ามีบางครั้งที่การทำงานthen()ของpipe().

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

แต่มีกรณีการใช้งานที่ต้องthen()ส่งคืนต้นฉบับDeferredที่ไม่สามารถทำได้pipe()เนื่องจากส่งคืนใหม่Promiseหรือไม่?


1
ฉันคิดเกี่ยวกับเรื่องนี้มาระยะหนึ่งแล้ว แต่ฉันไม่สามารถนึกถึงกรณีการใช้งานใด ๆ ได้ มันอาจจะเป็นค่าใช้จ่ายในการสร้างวัตถุสัญญาใหม่ถ้าคุณไม่ต้องการมัน (ฉันไม่รู้ว่าพวกมันถูกผูกมัดกันภายในอย่างไร) จะว่าไปมีคนที่เข้าใจเรื่องนี้ดีกว่าฉันอย่างแน่นอน
Felix Kling

6
ใครก็ตามที่สนใจคำถามนี้จะต้องสนใจ Ticket # 11010 ในตัวติดตามข้อผิดพลาด jQuery: MAKE
DEFERRED.THEN == DEFERRED PIPE

คำตอบ:


103

เนื่องจากjQuery 1.8 .thenทำงานเหมือนกับ.pipe:

ประกาศการเลิกใช้งาน:ตั้งแต่ jQuery 1.8 deferred.pipe()วิธีนี้เลิกใช้แล้ว deferred.then()วิธีการซึ่งแทนที่มันควรจะนำมาใช้แทน

และ

ในฐานะของ jQuery 1.8ที่deferred.then()วิธีการส่งกลับสัญญาใหม่ที่สามารถกรองสถานะและค่านิยมของรอการตัดบัญชีผ่านฟังก์ชั่นเปลี่ยนตอนนี้เลิกใช้deferred.pipe()วิธีการ

ตัวอย่างด้านล่างอาจเป็นประโยชน์สำหรับบางคน


พวกเขาตอบสนองวัตถุประสงค์ที่แตกต่างกัน:

  • .then()จะถูกใช้เมื่อใดก็ตามที่คุณต้องการทำงานกับผลลัพธ์ของกระบวนการเช่นตามเอกสารระบุเมื่ออ็อบเจ็กต์ที่รอการตัดบัญชีได้รับการแก้ไขหรือปฏิเสธ มันเหมือนกับการใช้.done()หรือ.fail().

  • คุณจะใช้.pipe()(ก่อน) กรองผลลัพธ์อย่างใด ค่าที่ส่งคืนของการโทรกลับไป.pipe()จะถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังdoneและการfailเรียกกลับ นอกจากนี้ยังสามารถส่งคืนอ็อบเจ็กต์อื่นที่รอการตัดบัญชีและการเรียกกลับต่อไปนี้จะถูกลงทะเบียนในรอการตัดบัญชีนี้

    ที่ไม่ได้เป็นกรณีที่มี.then()(หรือ.done(), .fail()) ค่าการกลับมาของการเรียกกลับที่ลงทะเบียนจะถูกละเว้นเพียง

ดังนั้นจะไม่ให้คุณใช้อย่างใดอย่างหนึ่ง หรือ.then() .pipe()คุณสามารถใช้.pipe()เพื่อวัตถุประสงค์เดียวกันกับ.then()แต่ converse ไม่ถือ


ตัวอย่าง 1

ผลลัพธ์ของการดำเนินการบางอย่างคืออาร์เรย์ของวัตถุ:

[{value: 2}, {value: 4}, {value: 6}]

และคุณต้องการคำนวณค่าต่ำสุดและสูงสุด สมมติว่าเราใช้การdoneเรียกกลับสองครั้ง:

deferred.then(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    var min = Math.min.apply(Math, values);

   /* do something with "min" */

}).then(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    var max = Math.max.apply(Math, values);

   /* do something with "max" */ 

});

ในทั้งสองกรณีคุณต้องวนซ้ำรายการและดึงค่าจากแต่ละออบเจ็กต์

จะดีกว่าไหมหากดึงค่าไว้ล่วงหน้าเพื่อที่คุณจะได้ไม่ต้องทำสิ่งนี้ในการเรียกกลับทั้งสองแบบทีละรายการ ใช่ และนั่นคือสิ่งที่เราสามารถใช้.pipe()สำหรับ:

deferred.pipe(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    return values; // [2, 4, 6]

}).then(function(result) {
    // result = [2, 4, 6]

    var min = Math.min.apply(Math, result);

    /* do something with "min" */

}).then(function(result) {
    // result = [2, 4, 6]

    var max = Math.max.apply(Math, result);

    /* do something with "max" */

});

เห็นได้ชัดว่านี่เป็นตัวอย่างที่สร้างขึ้นและมีหลายวิธี (อาจจะดีกว่า) ในการแก้ปัญหานี้ แต่ฉันหวังว่ามันจะอธิบายได้ตรงประเด็น


ตัวอย่าง 2

พิจารณาการโทรของ Ajax บางครั้งคุณต้องการเริ่มต้นการโทร Ajax หนึ่งครั้งหลังจากการโทรครั้งก่อนเสร็จสิ้น วิธีหนึ่งคือการโทรครั้งที่สองภายในการdoneโทรกลับ:

$.ajax(...).done(function() {
    // executed after first Ajax
    $.ajax(...).done(function() {
        // executed after second call
    });
});

ตอนนี้สมมติว่าคุณต้องการแยกรหัสของคุณและวางการโทร Ajax ทั้งสองนี้ไว้ในฟังก์ชัน:

function makeCalls() {
    // here we return the return value of `$.ajax().done()`, which
    // is the same deferred object as returned by `$.ajax()` alone

    return $.ajax(...).done(function() {
        // executed after first call
        $.ajax(...).done(function() {
            // executed after second call
        });
    });
}

คุณต้องการใช้ออบเจ็กต์รอการตัดบัญชีเพื่ออนุญาตรหัสอื่นที่เรียกmakeCallsให้แนบการโทรกลับสำหรับการโทร Ajax ครั้งที่สองแต่

makeCalls().done(function() {
    // this is executed after the first Ajax call
});

จะไม่ได้ผลตามที่ต้องการเนื่องจากการโทรครั้งที่สองทำภายในการdoneโทรกลับและไม่สามารถเข้าถึงได้จากภายนอก

วิธีแก้ปัญหาจะใช้.pipe()แทน:

function makeCalls() {
    // here we return the return value of `$.ajax().pipe()`, which is
    // a new deferred/promise object and connected to the one returned
    // by the callback passed to `pipe`

    return $.ajax(...).pipe(function() {
        // executed after first call
        return $.ajax(...).done(function() {
            // executed after second call
        });
    });
}

makeCalls().done(function() {
    // this is executed after the second Ajax call
});

โดยใช้.pipe()ตอนนี้คุณสามารถทำให้มันเป็นไปได้ที่จะผนวกการเรียกกลับไป "ภายใน" โทรอาแจ็กซ์โดยไม่ต้องเปิดเผยที่เกิดขึ้นจริงไหล / สั่งซื้อของสาย


โดยทั่วไปออบเจ็กต์ที่รอการตัดบัญชีเป็นวิธีที่น่าสนใจในการแยกรหัสของคุณ :)


อ่าใช่ฉันมองข้ามไปซึ่งpipeสามารถทำการกรองที่thenไม่สามารถทำได้ แต่ใน Googling หัวข้อเหล่านี้ดูเหมือนว่าพวกเขาเลือกที่จะเรียกมันpipeมากกว่าfilterเพราะถือว่าการกรองนั้นเป็นโบนัสพิเศษที่มาพร้อมกับมันในขณะที่pipeระบุจุดประสงค์ที่แท้จริงของมันอย่างชัดเจนมากขึ้น ดูเหมือนว่าควรมีความแตกต่างอื่น ๆ นอกเหนือจากการกรอง (แล้วอีกครั้งฉันยอมรับว่าฉันไม่ค่อยเข้าใจคุณลักษณะการกรองแม้แต่กับตัวอย่างของคุณควรresult values;เป็นไปreturn values;ตามนั้นหรือไม่)
hippietrail

เมื่อฉันบอกว่าฉันไม่เข้าใจตัวอย่างของคุณมันเป็นแบบนี้ไหมในตัวอย่างด้านบนทั้งสอง.then()ได้รับข้อมูลเดียวกันกับresultที่คุณกรองในแต่ละครั้ง ในขณะที่ในตัวอย่างด้านล่างนี้.pipe()จะลบข้อมูลบางส่วนในข้อมูลresultก่อนที่จะส่งต่อเนื่องจากresultทั้งสองข้อมูลที่ตามมา.then()จะได้รับ?
hippietrail

1
@hippietrail: ฉันปรับปรุงคำตอบของฉันในขณะเดียวกันและยังรวมถึงเพื่อวัตถุประสงค์อื่น ๆ .pipe()ของ หากการโทรกลับส่งคืนอ็อบเจ็กต์ที่รอการตัดบัญชีการเรียกกลับที่ทำหรือล้มเหลวในภายหลังจะถูกลงทะเบียนสำหรับอ็อบเจ็กต์นั้น ฉันจะรวมอีกตัวอย่างหนึ่ง แก้ไข:เกี่ยวกับความคิดเห็นที่สองของคุณ: ใช่
Felix Kling

ดังนั้นความแตกต่างอย่างหนึ่งก็คือข้อมูลไหลผ่าน pipe()ในขณะที่then()เหมือนโหนดลีฟในตอนท้ายซึ่งคุณต้องใช้ข้อมูลของคุณและจะไม่ไหลต่อไปและแม้ว่าจะthen()ส่งคืนข้อมูลที่Deferredไม่ได้ใช้จริง / มีประโยชน์? หากสิ่งนี้ถูกต้องอาจช่วยชี้แจงให้รวมบางอย่างเช่น/* do something with "min"/"max" */ในแต่ละ ".then () clause"
hippietrail

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

7

ไม่มีกรณีที่คุณต้องใช้then()มากกว่าpipe()นี้ คุณสามารถเลือกที่จะเพิกเฉยต่อค่าที่pipe()จะผ่านเข้ามาได้ตลอดเวลาการใช้งานอาจมีผลกระทบเล็กน้อยpipeแต่ก็ไม่น่าจะสำคัญ

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

มันเหมือนกับมีฟังก์ชันที่ส่งกลับค่าที่ไม่เคยใช้: ทำให้สับสน

ดังนั้นthen()ควรใช้เมื่อคุณควรและpipe()เมื่อคุณควร ...


3
ฉันได้พบตัวอย่างในชีวิตจริงโดยใช้ทั้งสองอย่างในบล็อกของ K. Scott Allen "การทดลองในการเขียน": Geolocation, Geocoding และ jQuery Promises : "จากนั้นตรรกะการควบคุมก็อ่านได้ดี:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "โปรดทราบว่าไปป์แตกต่างจาก เพราะไปป์ให้คำสัญญาใหม่กลับคืนมา "
hippietrail

5

ในความเป็นจริงปรากฎว่าความแตกต่างระหว่าง.then()และ.pipe()ได้รับการพิจารณาว่าไม่จำเป็นและถูกทำให้เหมือนกับ jQuery เวอร์ชัน 1.8

จากความคิดเห็นโดยjaubourgในตั๋วตัวติดตามข้อผิดพลาดของ jQuery # 11010 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A":

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

(เหมือง emphassis)


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