Quand dois-je utiliser la méthode «then» de jQuery deferred et quand dois-je utiliser la méthode «pipe»?

97

jQuery Deferreda deux fonctions qui peuvent être utilisées pour implémenter le chaînage asynchrone des fonctions:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Une fonction, ou un tableau de fonctions, appelée lorsque le différé est résolu.
failCallbacks Une fonction, ou un tableau de fonctions, appelée lorsque le Deferred est rejeté.

pipe()

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

doneFilter Une fonction facultative qui est appelée lorsque le différé est résolu.
failFilter Une fonction facultative qui est appelée lorsque le différé est rejeté.

Je sais que cela then()fait un peu plus longtemps que pipe()ce dernier doit ajouter un avantage supplémentaire, mais quelle est la différence précisément m'échappe. Les deux prennent à peu près les mêmes paramètres de rappel bien qu'ils diffèrent par leur nom et la différence entre renvoyer un Deferredet renvoyer un Promisesemble légère.

J'ai lu les documents officiels à maintes reprises, mais je les trouve toujours trop "denses" pour vraiment comprendre et la recherche a donné lieu à de nombreuses discussions sur l'une ou l'autre fonctionnalité, mais je n'ai rien trouvé qui clarifie vraiment les différentes avantages et inconvénients de chacun.

Alors, quand est-il préférable de l'utiliser thenet quand est-il préférable de l'utiliser pipe?


Une addition

L'excellente réponse de Felix a vraiment aidé à clarifier en quoi ces deux fonctions diffèrent. Mais je me demande s'il y a des moments où la fonctionnalité de then()est préférable à celle de pipe().

Il est évident que pipe()c'est plus puissant que then()et il semble que le premier puisse faire tout ce que le second peut faire. Une des raisons d'utiliser then()pourrait être que son nom reflète son rôle en tant que terminaison d'une chaîne de fonctions traitant les mêmes données.

Mais y a-t-il un cas d'utilisation qui nécessite then()le retour de l'original Deferredqui ne peut pas être fait pipe()car il renvoie un nouveauPromise ?

hippietrail
la source
1
J'y ai réfléchi pendant un moment, mais tbh, je ne peux penser à aucun cas d'utilisation. La création de nouveaux objets de promesse peut être une surcharge si vous n'en avez pas besoin (je ne sais pas comment ils sont enchaînés en interne). Cela dit, il y a certainement des gens qui comprennent mieux cela que moi.
Felix Kling
6
Toute personne intéressée par cette question sera sûrement intéressée par le ticket # 11010 sur le traqueur de bogues jQuery: MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A
hippietrail
1
Related: documentation pipe () et then () vs réalité dans jQuery 1.8
Bergi

Réponses:

103

Puisque jQuery 1.8 .then se comporte de la même manière que .pipe:

Avis de deferred.pipe()dépréciation : à partir de jQuery 1.8, la méthode est obsolète. La deferred.then()méthode, qui la remplace, doit être utilisée à la place.

et

À partir de jQuery 1.8 , la deferred.then()méthode retourne une nouvelle promesse qui peut filtrer le statut et les valeurs d'un différé via une fonction, remplaçant la deferred.pipe()méthode désormais obsolète .

Les exemples ci-dessous pourraient encore être utiles à certains.


Ils servent à des fins différentes:

  • .then()doit être utilisé chaque fois que vous souhaitez travailler avec le résultat du processus, c'est-à-dire comme le dit la documentation, lorsque l'objet différé est résolu ou rejeté. C'est la même chose que d'utiliser .done()ou .fail().

  • Vous utiliseriez .pipe()pour (pré) filtrer le résultat d'une manière ou d'une autre. La valeur de retour d'un callback à .pipe()sera transmise en argument aux callbacks doneet fail. Il peut également renvoyer un autre objet différé et les rappels suivants seront enregistrés sur ce différé.

    Ce n'est pas le cas avec .then()(ou .done(), .fail()), les valeurs de retour des rappels enregistrés sont simplement ignorées.

Ce n'est donc pas que vous utilisiez ni .then() ni .pipe() . Vous pouvez utiliser .pipe()aux mêmes fins que .then()mais l'inverse ne tient pas.


Exemple 1

Le résultat d'une opération est un tableau d'objets:

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

et vous voulez calculer le minimum et le maximum des valeurs. Supposons que nous utilisons deux donerappels:

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

});

Dans les deux cas, vous devez parcourir la liste et extraire la valeur de chaque objet.

Ne serait-il pas préférable d'extraire les valeurs à l'avance afin de ne pas avoir à le faire individuellement dans les deux rappels? Oui! Et c'est ce que nous pouvons utiliser .pipe()pour:

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

});

Évidemment, c'est un exemple inventé et il existe de nombreuses façons différentes (peut-être meilleures) de résoudre ce problème, mais j'espère que cela illustre ce point.


Exemple 2

Considérez les appels Ajax. Parfois, vous souhaitez lancer un appel Ajax après la fin d'un précédent. Une façon consiste à effectuer le deuxième appel dans un donerappel:

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

Supposons maintenant que vous souhaitiez découpler votre code et mettre ces deux appels Ajax dans une fonction:

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

Vous souhaitez utiliser l'objet différé pour permettre à un autre code qui appelle makeCallsde joindre des rappels pour le deuxième appel Ajax, mais

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

n'aurait pas l'effet escompté car le second appel est effectué à l'intérieur d'un donecallback et n'est pas accessible de l'extérieur.

La solution serait d'utiliser à la .pipe()place:

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

En utilisant, .pipe()vous pouvez maintenant rendre possible l'ajout de rappels à l'appel Ajax "interne" sans exposer le flux / l'ordre réel des appels.


En général, les objets différés offrent un moyen intéressant de découpler votre code :)

Félix Kling
la source
Ah oui, j'ai oublié que pipepeut faire du filtrage qui thenne peut pas faire. Mais en googlant ces sujets, il semble qu'ils aient choisi de l'appeler pipeplutôt que filterparce qu'ils considéraient le filtrage comme un bonus supplémentaire qui l'accompagnait alors qu'il pipeindiquait plus clairement son véritable objectif. Il semble donc qu'il devrait y avoir d'autres différences en plus du filtrage. (Encore une fois, j'avoue que je ne comprends pas vraiment la fonction de filtrage, même avec vos exemples. Devrait result values;être return values;au fait?)
hippietrail
Quand je dis que je ne comprends pas vos exemples, est-ce quelque chose comme ceci: dans l'exemple du haut, les deux .then()reçoivent les mêmes données dans resultlesquelles vous filtrez à chaque fois; alors que dans l'exemple inférieur, le .pipe()supprime certaines des données dans son resultavant de les transmettre comme resultles deux suivants .then()recevront?
hippietrail
1
@hippietrail: j'ai mis à jour ma réponse entre-temps et j'ai également inclus les autres objectifs de .pipe(). Si le rappel retourne un objet différé, les rappels effectués ou échoués suivants seront enregistrés pour cet objet. Je vais inclure un autre exemple. edit: concernant votre deuxième commentaire: oui.
Felix Kling
Donc , une différence est que les données circulent à travers pipe() alors then()est plus comme un nœud feuille à la fin de laquelle vous devez utiliser vos données et ne coule pas plus loin, et que , malgré le fait que le then()rendement d' un Deferredil est pas réellement utilisé / utile? Si cela est correct, il peut être utile de clarifier pour inclure quelque chose comme /* do something with "min"/"max" */dans chaque "clause .then ()".
hippietrail
1
Pas de soucis :) Il m'a fallu un certain temps aussi pour bien comprendre comment fonctionnent les objets différés et leurs méthodes. Mais une fois que vous l'avez compris, cela ne semble plus être difficile. Je conviens que la documentation pourrait probablement être rédigée de manière plus simple.
Felix Kling
7

Il n'y a aucun cas où vous DEVEZ utiliser then()over pipe(). Vous pouvez toujours choisir d'ignorer la valeur qui pipe()passera. Il peut y avoir un léger impact sur les performances de l'utilisation pipe, mais il est peu probable que cela ait une importance.

Il peut donc sembler que vous pouvez toujours utiliser pipe()dans les deux cas. Cependant , en utilisant pipe(), vous communiquez à d'autres personnes lisant votre code (y compris vous-même, dans six mois) qu'il y a une certaine importance à la valeur de retour. Si vous le rejetez, vous violez cette construction sémantique.

C'est comme avoir une fonction qui renvoie une valeur qui n'est jamais utilisée: déroutant.

Alors utilisez then()quand vous devriez, et pipe()quand vous devriez ...

Alex Feinman
la source
3
J'ai trouvé un exemple réel utilisant les deux sur le blog de K. Scott Allen, "Experiments In Writing": Geolocation, Geocoding, and jQuery Promises : "Alors la logique de contrôle se lit plutôt bien:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "Notez que le tube est différent de puis parce que pipe rend une nouvelle promesse. "
hippietrail
5

En fait, il s'avère que la différence entre .then()et .pipe()a été jugée inutile et qu'ils ont été rendus identiques à ceux de la version 1.8 de jQuery.

D'après un commentaire dejaubourg dans le ticket de suivi des bogues de jQuery # 11010 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A":

Dans la version 1.8, nous supprimerons l'ancien et le remplacerons par le tuyau actuel. Mais la conséquence très triste est que nous devrons dire aux gens d'utiliser le fait, l'échec et le progrès non standard, car la proposition ne fournit pas de moyen simple, EFFICACE, d'ajouter simplement un rappel.

(mine d'emphassis)

hippietrail
la source
1
Meilleure référence à ce jour, je recherche des usages avancés.
TWiStErRob