Kapan saya harus menggunakan metode "kemudian" jQuery deferred dan kapan saya harus menggunakan metode "pipa"?

97

jQuery Deferredmemiliki dua fungsi yang dapat digunakan untuk mengimplementasikan rangkaian fungsi asinkron:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Sebuah fungsi, atau larik fungsi, dipanggil saat Deferred diselesaikan.
failCallbacks Sebuah fungsi, atau larik fungsi, dipanggil saat Deferred ditolak.

pipe()

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

doneFilter Fungsi opsional yang dipanggil saat Deferred diselesaikan.
failFilter Fungsi opsional yang disebut ketika tangguhan ditolak.

Saya tahu then()telah ada sedikit lebih lama dari pipe()itu yang terakhir harus menambahkan beberapa manfaat tambahan, tetapi apa perbedaan yang sebenarnya tidak saya ketahui. Keduanya mengambil hampir semua parameter callback yang sama meskipun mereka berbeda dalam nama dan perbedaan antara mengembalikan Deferreddan mengembalikan Promisenampaknya sedikit.

Saya telah membaca dokumen resmi berulang kali tetapi selalu merasa terlalu "padat" untuk benar-benar membungkus kepala saya dan pencarian telah menemukan banyak diskusi tentang satu fitur atau yang lain tetapi saya belum menemukan apa pun yang benar-benar menjelaskan perbedaannya pro dan kontra masing-masing.

Jadi kapan lebih baik digunakan thendan kapan lebih baik digunakan pipe?


Tambahan

Jawaban Felix yang luar biasa benar-benar membantu memperjelas bagaimana kedua fungsi ini berbeda. Tapi saya bertanya-tanya apakah ada kalanya fungsionalitas then()lebih disukai daripada pipe().

Jelas bahwa pipe()itu lebih kuat daripada then()dan tampaknya yang pertama dapat melakukan apa pun yang dapat dilakukan oleh yang terakhir. Salah satu alasan untuk menggunakan then()mungkin karena namanya mencerminkan perannya sebagai penghentian rantai fungsi yang memproses data yang sama.

Tetapi apakah ada kasus penggunaan yang mengharuskan then()pengembalian yang asli Deferredyang tidak dapat dilakukan pipe()karena mengembalikan yang baru Promise?

hippietrail
sumber
1
Saya memikirkan hal ini sebentar, tetapi tbh, saya tidak dapat memikirkan kasus penggunaan apa pun. Mungkin hanya overhead untuk membuat objek promise baru jika Anda tidak membutuhkannya (saya tidak tahu bagaimana mereka dirangkai secara internal). Bisa dikatakan, pasti ada orang yang memiliki pemahaman yang lebih baik tentang ini daripada saya.
Felix Kling
6
Siapa pun yang tertarik dengan pertanyaan ini pasti akan tertarik dengan Tiket # 11010 di jQuery bug tracker: MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A
hippietrail

Jawaban:

103

Karena jQuery 1.8 .then berperilaku sama seperti .pipe:

Pemberitahuan Penghentian: Mulai jQuery 1.8, deferred.pipe()metode ini tidak digunakan lagi. The deferred.then()metode, yang menggantikan itu, harus digunakan sebagai gantinya.

dan

Mulai jQuery 1.8 , deferred.then()metode ini mengembalikan janji baru yang dapat memfilter status dan nilai ditangguhkan melalui fungsi, menggantikan metode yang sekarang tidak deferred.pipe()digunakan lagi .

Contoh di bawah ini mungkin masih berguna bagi sebagian orang.


Mereka melayani tujuan yang berbeda:

  • .then()digunakan kapan pun Anda ingin bekerja dengan hasil dari proses tersebut, misalnya seperti yang dikatakan dalam dokumentasi, saat objek yang ditangguhkan diselesaikan atau ditolak. Ini sama dengan menggunakan .done()atau .fail().

  • Anda akan menggunakan .pipe()untuk (pra) memfilter hasilnya. Nilai kembali dari panggilan balik ke .pipe()akan diteruskan sebagai argumen ke donedan failpanggilan balik. Itu juga dapat mengembalikan objek ditangguhkan lainnya dan panggilan balik berikut akan didaftarkan pada ditangguhkan ini.

    Tidak demikian halnya dengan .then() (atau .done(), .fail()), nilai kembalian dari callback terdaftar hanya diabaikan.

Jadi, Anda tidak menggunakan salah satu .then() atau .pipe() . Kamu dapat menggunakan .pipe()untuk tujuan yang sama .then()tetapi sebaliknya tidak berlaku.


Contoh 1

Hasil dari beberapa operasi adalah larik objek:

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

dan Anda ingin menghitung nilai minimum dan maksimum. Mari kita asumsikan kita menggunakan duadone callback:

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" */ 

});

Dalam kedua kasus, Anda harus mengulang daftar dan mengekstrak nilai dari setiap objek.

Bukankah lebih baik untuk mengekstrak nilai sebelumnya sehingga Anda tidak perlu melakukan ini di kedua callback secara individual? Iya! Dan itulah yang bisa kita gunakan .pipe()untuk:

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" */

});

Jelas ini adalah contoh yang dibuat-buat dan ada banyak cara berbeda (mungkin lebih baik) untuk menyelesaikan masalah ini, tapi saya harap ini menggambarkan maksudnya.


Contoh 2

Pertimbangkan panggilan Ajax. Terkadang Anda ingin memulai satu panggilan Ajax setelah panggilan sebelumnya selesai. Salah satu caranya adalah dengan melakukan panggilan kedua di dalam donecallback:

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

Sekarang mari kita asumsikan Anda ingin memisahkan kode Anda dan menempatkan dua panggilan Ajax ini di dalam sebuah fungsi:

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
        });
    });
}

Anda ingin menggunakan objek deferred untuk mengizinkan kode lain yang memanggil makeCallsuntuk melampirkan callback untuk panggilan Ajax kedua , tapi

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

tidak akan memiliki efek yang diinginkan karena panggilan kedua dilakukan di dalam donepanggilan balik dan tidak dapat diakses dari luar.

Solusinya adalah dengan menggunakan .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
});

Dengan menggunakan, .pipe()Anda sekarang dapat memungkinkan untuk menambahkan callback ke panggilan Ajax "dalam" tanpa menunjukkan aliran / urutan panggilan yang sebenarnya.


Secara umum, objek yang ditangguhkan memberikan cara yang menarik untuk memisahkan kode Anda :)

Felix Kling
sumber
Ah ya saya lupa bahwa pipebisa melakukan penyaringan yang thentidak bisa dilakukan. Tetapi dalam Googling topik-topik ini tampaknya mereka memilih untuk menyebutnya pipedaripada filterkarena menganggap penyaringan sebagai sesuatu dari bonus tambahan yang menyertainya sedangkan pipelebih jelas menunjukkan tujuan sebenarnya. Jadi sepertinya harus ada perbedaan lain selain filtering. (Kemudian lagi Aku mengakui bahwa aku tidak benar-benar memahami fitur penyaringan bahkan dengan contoh Anda Harus. result values;Menjadi return values;by the way?)
hippietrail
Ketika saya mengatakan saya tidak mengerti contoh Anda, apakah itu seperti ini: Pada contoh di atas, keduanya .then()menerima data yang sama di resultmana Anda memfilter setiap kali; sedangkan pada contoh yang lebih rendah, .pipe()menghapus beberapa data resultsebelum meneruskannya sebagai resultdua berikutnya .then()akan menerima?
hippietrail
1
@hippietrail: Sementara itu saya memperbarui jawaban saya dan juga menyertakan tujuan lain dari .pipe(). Jika callback mengembalikan objek yang ditangguhkan, callback selesai atau gagal berikutnya akan didaftarkan untuk objek itu. Saya akan menyertakan contoh lain. edit: tentang komentar kedua Anda: ya.
Felix Kling
Jadi satu perbedaan adalah bahwa data mengalir melalui pipe() sedangkan then()lebih seperti simpul daun pada akhirnya Anda harus menggunakan data Anda dan tidak mengalir lebih jauh, dan meskipun fakta bahwa then()mengembalikan Deferreditu tidak benar-benar digunakan / berguna? Jika ini benar, mungkin membantu untuk memperjelas untuk menyertakan sesuatu seperti /* do something with "min"/"max" */di setiap "klausa .then ()".
hippietrail
1
Jangan khawatir :) Butuh beberapa waktu juga bagi saya untuk memahami sepenuhnya cara kerja objek yang ditangguhkan dan metode mereka. Tapi begitu Anda memahaminya, sepertinya tidak akan sulit lagi. Saya setuju bahwa dokumentasinya mungkin bisa ditulis dengan cara yang lebih mudah.
Felix Kling
7

Tidak ada kasus di mana Anda HARUS menggunakan then()lebih pipe(). Anda selalu dapat memilih untuk mengabaikan nilai yang pipe()akan diteruskan. Mungkin ada sedikit kinerja yang menurun untuk digunakan pipe- tetapi itu tidak menjadi masalah.

Jadi sepertinya Anda selalu bisa menggunakan pipe()dalam kedua kasus. Namun , dengan menggunakan pipe(), Anda berkomunikasi dengan orang lain yang membaca kode Anda (termasuk Anda sendiri, enam bulan dari sekarang) bahwa ada beberapa hal penting untuk nilai balik. Jika Anda membuangnya, Anda melanggar konstruksi semantik ini.

Ini seperti memiliki fungsi yang mengembalikan nilai yang tidak pernah digunakan: membingungkan.

Jadi gunakan then()saat Anda harus, dan pipe()kapan Anda harus ...

Alex Feinman
sumber
3
Saya telah menemukan contoh kehidupan nyata menggunakan keduanya di blog K. Scott Allen, "Eksperimen Dalam Penulisan": Geolocation, Geocoding, dan jQuery Promises : "Kemudian logika kontrol terbaca dengan cukup baik:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "Perhatikan bahwa pipa berbeda dari lalu karena pipa mengembalikan janji baru. "
hippietrail
5

Ternyata perbedaan antara .then()dan .pipe()dianggap tidak perlu dan telah dibuat sama dengan jQuery versi 1.8.

Dari komentar olehjaubourg di tiket pelacak bug jQuery # 11010 "BUAT DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A":

Di 1.8, kami akan menghapus yang lama dan menggantinya dengan pipa saat ini. Tetapi konsekuensi yang sangat menyedihkan adalah kita harus memberitahu orang-orang untuk menggunakan non-standar done, fail and progress, karena proposal tidak memberikan yang sederhana, EFISIEN, maksudnya hanya menambahkan callback.

(tambang emfassis)

hippietrail
sumber
1
Referensi terbaik sejauh ini, saya sedang mencari penggunaan lanjutan.
TWiStErRob