Quando dovrei usare il metodo "then" di jQuery deferred e quando dovrei usare il metodo "pipe"?


97

jQuery Deferredha due funzioni che possono essere utilizzate per implementare il concatenamento asincrono di funzioni:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Una funzione, o array di funzioni, chiamata quando il Deferred viene risolto.
failCallbacks Una funzione, o array di funzioni, chiamata quando il Deferred viene rifiutato.

pipe()

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

doneFilter Una funzione facoltativa che viene chiamata quando il Deferred viene risolto.
failFilter Una funzione facoltativa chiamata quando Deferred viene rifiutato.

So che esiste da then()un po 'di più, pipe()quindi quest'ultimo deve aggiungere qualche vantaggio in più, ma quale sia la differenza precisamente mi sfugge. Entrambi accettano praticamente gli stessi parametri di callback sebbene differiscano nel nome e la differenza tra la restituzione di a Deferrede la restituzione di a Promisesembra lieve.

Ho letto i documenti ufficiali più e più volte ma li trovo sempre troppo "densi" per capire davvero e la ricerca ha trovato molte discussioni sull'una o sull'altra funzionalità ma non ho trovato nulla che chiarisca veramente le diverse pro e contro di ciascuno.

Quindi quando è meglio usare thene quando è meglio usare pipe?


Aggiunta

L'eccellente risposta di Felix ha davvero aiutato a chiarire come differiscono queste due funzioni. Ma mi chiedo se ci siano momenti in cui la funzionalità di then()è preferibile a quella di pipe().

È evidente che pipe()è più potente then()e sembra che il primo possa fare tutto ciò che può fare il secondo. Un motivo per utilizzarlo then()potrebbe essere che il suo nome riflette il suo ruolo di terminazione di una catena di funzioni che elaborano gli stessi dati.

Ma c'è un caso d'uso che richiede then()la restituzione dell'originale Deferredche non può essere risolto a pipe()causa della restituzione di un nuovo Promise?


1
Ci ho pensato per un po ', ma tbh, non riesco a pensare a nessun caso d'uso. Potrebbe essere solo un sovraccarico per creare nuovi oggetti promessa se non ne hai bisogno (non so come siano concatenati internamente). Detto questo, ci sono sicuramente persone che ne hanno una migliore comprensione di me.
Felix Kling

6
Chiunque sia interessato a questa domanda sarà sicuramente interessato al Ticket # 11010 sul bug tracker di jQuery: MAKE DIFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A
hippietrail

Risposte:


103

Poiché jQuery 1.8 .then si comporta come .pipe:

Avviso di deferred.pipe()deprecazione : a partire da jQuery 1.8, il metodo è deprecato. Il deferred.then()metodo, che lo sostituisce, dovrebbe invece essere utilizzato.

e

A partire da jQuery 1.8 , il deferred.then()metodo restituisce una nuova promessa che può filtrare lo stato ei valori di un differito tramite una funzione, sostituendo il deferred.pipe()metodo ormai deprecato .

Gli esempi seguenti potrebbero essere ancora utili per alcuni.


Servono a scopi diversi:

  • .then()deve essere utilizzato ogni volta che si desidera lavorare con il risultato del processo, cioè come dice la documentazione, quando l'oggetto differito viene risolto o rifiutato. È lo stesso che usare .done()o .fail().

  • Useresti .pipe()per (pre) filtrare il risultato in qualche modo. Il valore restituito di un callback a .pipe()verrà passato come argomento ai callback donee fail. Può anche restituire un altro oggetto differito e i seguenti callback verranno registrati su questo differito.

    Questo non è il caso di .then()(o .done(), .fail()), i valori di ritorno dei callback registrati vengono semplicemente ignorati.

Quindi non è che si utilizza sia .then() o .pipe() . Si potrebbe utilizzare .pipe()per gli stessi scopi .then(), ma il contrario non regge.


Esempio 1

Il risultato di alcune operazioni è un array di oggetti:

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

e si desidera calcolare il minimo e il massimo dei valori. Supponiamo di utilizzare due donecallback:

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

});

In entrambi i casi è necessario scorrere l'elenco ed estrarre il valore da ogni oggetto.

Non sarebbe meglio estrarre in qualche modo i valori in anticipo in modo da non doverlo fare individualmente in entrambi i callback? Sì! Ed è quello che possiamo usare .pipe()per:

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

});

Ovviamente questo è un esempio inventato e ci sono molti modi diversi (forse migliori) per risolvere questo problema, ma spero che illustri il punto.


Esempio 2

Considera le chiamate Ajax. A volte si desidera avviare una chiamata Ajax dopo il completamento di una precedente. Un modo è effettuare la seconda chiamata all'interno di una donerichiamata:

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

Supponiamo ora che tu voglia disaccoppiare il tuo codice e inserire queste due chiamate Ajax all'interno di una funzione:

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

Si desidera utilizzare l'oggetto differito per consentire ad altro codice che chiama makeCallsdi allegare callback per la seconda chiamata Ajax, ma

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

non avrebbe l'effetto desiderato in quanto la seconda chiamata viene effettuata all'interno di una donerichiamata e non accessibile dall'esterno.

La soluzione sarebbe usare .pipe()invece:

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

Usando .pipe()è ora possibile rendere possibile aggiungere callback alla chiamata Ajax "interna" senza esporre il flusso / ordine effettivo delle chiamate.


In generale, gli oggetti differiti forniscono un modo interessante per disaccoppiare il tuo codice :)


Ah sì, ho trascurato che pipepuò fare il filtraggio che thennon può fare. Ma in Google questi argomenti sembra che abbiano scelto di chiamarli pipepiuttosto che filterperché consideravano il filtraggio una sorta di bonus extra che ne derivava, mentre pipeindicava più chiaramente il suo vero scopo. Quindi sembra che dovrebbero esserci altre differenze oltre al filtro. (Poi di nuovo ammetto che non capisco davvero la funzione di filtraggio anche con i tuoi esempi. Dovrebbe result values;essere return values;a proposito?)
hippietrail

Quando dico che non capisco i vostri esempi, è qualcosa del genere: Nell'esempio in alto, i due .then()ricevono gli stessi dati in resultcui filtrate ogni volta; mentre nell'esempio in basso, il .pipe()rimuove alcuni dei dati nel suo resultprima di trasmetterli come riceveranno resulti due successivi .then()?
Hippietrail

1
@hippietrail: nel frattempo ho aggiornato la mia risposta includendo anche gli altri scopi di .pipe(). Se il callback restituisce un oggetto posticipato, i callback successivi eseguiti o non riusciti verranno registrati per quell'oggetto. Includerò un altro esempio. modifica: per quanto riguarda il tuo secondo commento: sì.
Felix Kling

Così una differenza è che i flussi di dati attraverso pipe() mentre then()è più simile a un nodo foglia al termine del quale è necessario utilizzare i dati e non scorre più avanti, e che, nonostante il fatto che then()i rendimenti di una Deferrednon è effettivamente utilizzato / utile? Se è corretto, potrebbe essere utile chiarire di includere qualcosa di simile /* do something with "min"/"max" */in ciascuna "clausola .then ()".
Hippietrail

1
Nessun problema :) Mi ci è voluto del tempo anche per comprendere appieno come funzionano gli oggetti differiti ei loro metodi. Ma una volta capito, non sembra più essere difficile. Sono d'accordo che la documentazione potrebbe probabilmente essere scritta in un modo più semplice.
Felix Kling

7

Non c'è nessun caso in cui DEVI usare then()sopra pipe(). Puoi sempre scegliere di ignorare il valore che pipe()verrà trasferito. Potrebbe esserci un leggero calo delle prestazioni per l'utilizzo pipe, ma è improbabile che abbia importanza.

Quindi potrebbe sembrare che tu possa semplicemente usare sempre pipe()in entrambi i casi. Tuttavia , utilizzando pipe(), stai comunicando ad altre persone che leggono il tuo codice (incluso te stesso, tra sei mesi) che c'è una certa importanza per il valore restituito. Se lo stai scartando, stai violando questo costrutto semantico.

È come avere una funzione che restituisce un valore che non viene mai utilizzato: confonde.

Quindi usa then()quando dovresti e pipe()quando dovresti ...


3
Ho trovato un esempio di vita reale utilizzando i due sul blog di K. Scott Allen, "Experiments In Writing": Geolocation, Geocoding, and jQuery Promises : "Allora la logica di controllo si legge abbastanza bene:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "Nota che pipe è diverso da poi perché pipe restituisce una nuova promessa ".
Hippietrail

5

In effetti si scopre che la differenza tra .then()e .pipe()è stata ritenuta non necessaria e sono state fatte per essere le stesse della versione 1.8 di jQuery.

Da un commentojaubourg nel ticket bug tracker di jQuery # 11010 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A":

In 1.8, rimuoveremo il vecchio quindi e lo sostituiremo con il tubo corrente. Ma la conseguenza molto triste è che dovremo dire alle persone di usare il fatto non standard, fallire e progredire, perché la proposta non fornisce un mezzo semplice, EFFICIENTE, per aggiungere solo un richiamo.

(enfatizza il mio)


1
Miglior riferimento finora, cerco utilizzi avanzati.
TWiStErRob
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.