Wann sollte ich die "then" -Methode von jQuery deferred verwenden und wann sollte ich die "pipe" -Methode verwenden?

97

jQuery's Deferredverfügt über zwei Funktionen, mit denen die asynchrone Verkettung von Funktionen implementiert werden kann:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Eine Funktion oder ein Array von Funktionen, die aufgerufen wird, wenn der verzögerte Wert aufgelöst wird.
failCallbacks Eine Funktion oder ein Array von Funktionen, die aufgerufen wird, wenn Deferred abgelehnt wird.

pipe()

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

doneFilter Eine optionale Funktion, die aufgerufen wird, wenn Deferred aufgelöst wird.
failFilter Eine optionale Funktion, die aufgerufen wird, wenn Deferred abgelehnt wird.

Ich weiß, dass then()es schon etwas länger her ist, pipe()daher muss letzteres einen zusätzlichen Nutzen bringen, aber was genau der Unterschied ist, entgeht mir. Beide verwenden fast dieselben Rückrufparameter, obwohl sie sich im Namen unterscheiden und der Unterschied zwischen der Rückgabe von a Deferredund der Rückgabe von a Promisegering erscheint.

Ich habe die offiziellen Dokumente immer und immer wieder gelesen, finde sie aber immer zu "dicht", um mich wirklich zu beschäftigen, und die Suche hat viele Diskussionen über das eine oder andere Feature ergeben, aber ich habe nichts gefunden, was den Unterschied wirklich verdeutlicht Vor- und Nachteile von jedem.

Wann ist es besser zu verwenden thenund wann ist es besser zu verwenden pipe?


Zusatz

Felix 'hervorragende Antwort hat wirklich geholfen zu klären, wie sich diese beiden Funktionen unterscheiden. Aber ich frage mich, ob es Zeiten gibt, in denen die Funktionalität von der von then()vorzuziehen ist pipe().

Es ist offensichtlich, dass dies pipe()mächtiger ist als then()und es scheint, dass Ersteres alles kann, was Letzteres tun kann. Ein Grund für die Verwendung then()könnte sein, dass sein Name seine Rolle als Beendigung einer Funktionskette widerspiegelt, die dieselben Daten verarbeitet.

Aber gibt es einen Anwendungsfall, bei dem then()das Original zurückgegeben Deferredwerden muss, was pipe()aufgrund der Rückgabe eines neuen Originals nicht möglich ist Promise?

Hippietrail
quelle
1
Ich habe eine Weile darüber nachgedacht, aber ich kann mir keinen Anwendungsfall vorstellen. Das Erstellen neuer Versprechungsobjekte ist möglicherweise nur ein Aufwand, wenn Sie sie nicht benötigen (ich weiß nicht, wie sie intern miteinander verkettet sind). Das heißt, es gibt sicherlich Leute, die dies besser verstehen als ich.
Felix Kling
6
Jeder, der sich für diese Frage interessiert, wird sich sicherlich für Ticket # 11010 auf dem jQuery-Bug-Tracker interessieren: MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A
Hippietrail

Antworten:

103

Da sich jQuery 1.8 .then genauso verhält wie .pipe:

Verfallshinweis: Ab jQuery 1.8 ist die deferred.pipe()Methode veraltet. Das deferred.then()Verfahren, das es ersetzt, sollte stattdessen verwendet werden.

und

Ab jQuery 1.8 gibt die deferred.then()Methode ein neues Versprechen zurück, mit dem der Status und die Werte einer durch eine Funktion zurückgestellten deferred.pipe()Methode gefiltert werden können, wobei die jetzt veraltete Methode ersetzt wird.

Die folgenden Beispiele könnten für einige noch hilfreich sein.


Sie dienen verschiedenen Zwecken:

  • .then()ist immer dann zu verwenden, wenn Sie mit dem Ergebnis des Prozesses arbeiten möchten, dh wie in der Dokumentation angegeben, wenn das zurückgestellte Objekt aufgelöst oder abgelehnt wird. Es ist dasselbe wie mit .done()oder .fail().

  • Sie würden das Ergebnis irgendwie .pipe()(vor) filtern . Der Rückgabewert eines Rückrufs an .pipe()wird als Argument an die Rückrufe doneund übergeben fail. Es kann auch ein anderes zurückgestelltes Objekt zurückgeben, und die folgenden Rückrufe werden für dieses zurückgestellte Objekt registriert.

    Das ist nicht der Fall mit .then()(oder .done(), .fail()), die Rückgabewerte der registrierten Rückrufe werden einfach ignoriert.

Es ist also nicht so, dass Sie entweder .then() oder verwenden .pipe() . Du könnten verwenden .pipe()als für die gleichen Zwecke , .then()aber die Umkehrung nicht gilt.


Beispiel 1

Das Ergebnis einer Operation ist ein Array von Objekten:

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

und Sie möchten das Minimum und Maximum der Werte berechnen. Nehmen wir an, wir verwenden zweidone Rückrufe:

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 beiden Fällen müssen Sie die Liste durchlaufen und den Wert aus jedem Objekt extrahieren.

Wäre es nicht besser, die Werte vorher irgendwie zu extrahieren, damit Sie dies nicht in beiden Rückrufen einzeln tun müssen? Ja! Und dafür können wir Folgendes verwenden .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" */

});

Natürlich ist dies ein erfundenes Beispiel und es gibt viele verschiedene (vielleicht bessere) Möglichkeiten, um dieses Problem zu lösen, aber ich hoffe, es veranschaulicht den Punkt.


Beispiel 2

Betrachten Sie Ajax-Anrufe. Manchmal möchten Sie einen Ajax-Aufruf einleiten, nachdem ein vorheriger abgeschlossen wurde. Eine Möglichkeit besteht darin, den zweiten Anruf innerhalb eines doneRückrufs zu tätigen :

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

Nehmen wir nun an, Sie möchten Ihren Code entkoppeln und diese beiden Ajax-Aufrufe in eine Funktion einfügen:

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

Sie möchten das zurückgestellte Objekt verwenden, um anderen Code, der aufruft makeCalls, das Anhängen von Rückrufen für den zweiten Ajax-Aufruf zu ermöglichen

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

hätte nicht den gewünschten Effekt, da der zweite Anruf innerhalb eines doneRückrufs getätigt wird und von außen nicht zugänglich ist.

Die Lösung wäre .pipe()stattdessen:

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

Mithilfe von können .pipe()Sie jetzt Rückrufe an den "inneren" Ajax-Anruf anhängen, ohne den tatsächlichen Ablauf / die Reihenfolge der Anrufe offenzulegen.


Im Allgemeinen bieten zurückgestellte Objekte eine interessante Möglichkeit, Ihren Code zu entkoppeln :)

Felix Kling
quelle
Ah ja, ich habe übersehen, dass pipeFilterung thennicht möglich ist. Aber beim Googeln dieser Themen scheinen sie sich dafür entschieden zu haben, es zu nennen, pipeanstatt filterdie Filterung als eine Art Bonus-Extra zu betrachten, das damit einherging , während pipeder wahre Zweck deutlicher angegeben wurde. Es scheint also, dass es neben der Filterung noch andere Unterschiede geben sollte. ( result values;return values;
Andererseits
Wenn ich sage, ich verstehe Ihre Beispiele nicht, ist es ungefähr so: Im oberen Beispiel erhalten die beiden .then()die gleichen Daten, in resultdenen Sie jedes Mal filtern; wohingegen im unteren Beispiel .pipe()einige der Daten entfernt werden, resultbevor sie weitergegeben werden, da resultdie beiden folgenden .then()s empfangen werden?
Hippietrail
1
@hippietrail: Ich habe meine Antwort in der Zwischenzeit aktualisiert und auch die anderen Zwecke von aufgenommen .pipe(). Wenn der Rückruf ein zurückgestelltes Objekt zurückgibt, werden nachfolgende erledigte oder fehlgeschlagene Rückrufe für dieses Objekt registriert. Ich werde ein weiteres Beispiel hinzufügen. bearbeiten: bezüglich Ihres zweiten Kommentars: ja.
Felix Kling
So ein Unterschied besteht darin , dass die Datenströme durch pipe() , während then()eher wie ein Blattknoten am Ende der Sie Ihre Daten verwenden müssen und es fließt nicht weiter, und das trotz der Tatsache , dass then()Renditen ein Deferredes tatsächlich nicht verwendet / sinnvoll? Wenn dies richtig ist, kann es hilfreich sein, zu verdeutlichen, dass /* do something with "min"/"max" */in jede ".then () -Klausel" so etwas aufgenommen wird.
Hippietrail
1
Keine Sorge :) Ich habe auch einige Zeit gebraucht, um zu verstehen, wie verzögerte Objekte und ihre Methoden funktionieren. Aber sobald Sie es verstanden haben, scheint es nicht mehr schwierig zu sein. Ich bin damit einverstanden, dass die Dokumentation wahrscheinlich einfacher geschrieben werden könnte.
Felix Kling
7

Es gibt keinen Fall, in dem Sie then()über verwenden müssen pipe(). Sie können jederzeit festlegen, dass der übergebene Wert ignoriert pipe()wird. Bei der Verwendung kann es zu geringfügigen Leistungseinbußen kommen pipe- dies ist jedoch unwahrscheinlich.

Es könnte also so aussehen, als könnten Sie pipe()in beiden Fällen einfach immer verwenden . Durch die Verwendung von pipe()kommunizieren Sie jedoch anderen Personen, die Ihren Code lesen (einschließlich Ihnen selbst, in sechs Monaten), dass der Rückgabewert eine gewisse Bedeutung hat. Wenn Sie es verwerfen, verletzen Sie dieses semantische Konstrukt.

Es ist, als hätte man eine Funktion, die einen Wert zurückgibt, der niemals verwendet wird: verwirrend.

Verwenden then()Sie also, wann Sie sollten und pipe()wann Sie ...

Alex Feinman
quelle
3
Ich habe ein reales Beispiel mit den beiden auf K. Scott Allens Blog "Experiments In Writing" gefunden: Geolocation, Geocoding und jQuery Promises : "Dann liest sich die Steuerlogik ziemlich gut:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "Beachten Sie, dass Pipe anders ist als dann, weil Pipe ein neues Versprechen zurückgibt. "
Hippietrail
5

Tatsächlich stellt sich heraus, dass der Unterschied zwischen .then()und .pipe()als unnötig erachtet wurde und dass er mit jQuery Version 1.8 identisch ist.

Aus einem Kommentar vonjaubourg in jQuerys Bug-Tracker- Ticket # 11010 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A":

In 1.8 werden wir dann das alte entfernen und es durch das aktuelle Rohr ersetzen. Die sehr sadende Konsequenz ist jedoch, dass wir den Leuten sagen müssen, dass sie das Nicht-Standard-Fertigstellen, Scheitern und Fortschreiten verwenden sollen, da der Vorschlag keine einfache, EFFIZIENTE Möglichkeit bietet, nur einen Rückruf hinzuzufügen.

(Emphassis Mine)

Hippietrail
quelle
1
Beste Referenz bisher, ich suche nach fortgeschrittenen Verwendungen.
TWiStErRob