jQuery Deferred
a deux fonctions qui peuvent être utilisées pour implémenter le chaînage asynchrone de fonctions:
then()
deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred
doneCallbacks Une fonction, ou tableau de fonctions, appelée lorsque le différé est résolu.
failCallbacks Une fonction, ou tableau de fonctions, appelée lorsque le différé 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 then()
existe depuis un peu plus longtemps que pipe()
donc cette dernière doit ajouter un avantage supplémentaire, mais quelle est précisément la différence me é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 Deferred
et renvoyer un Promise
semble faible.
J'ai lu les documents officiels encore et encore, mais je les trouve toujours trop "denses" pour vraiment envelopper ma tête et la recherche a trouvé beaucoup de discussions sur l'une ou l'autre fonctionnalité mais je n'ai rien trouvé qui clarifie vraiment les différents avantages et inconvénients de chacun.
Alors, quand est-il préférable d'utiliser then
et quand est-il préférable d'utiliser pipe
?
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()
est plus puissant que then()
et il semble que le premier puisse faire tout ce que le second peut faire. L'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 que then()
retourne le Deferred
original qui ne peut pas être fait avec pipe()
à cause de cela renvoyer un nouveau Promise
?
Puisque jQuery 1.8.then
Se comporte de la même manière que .pipe
:
Avis de dépréciation: Depuis jQuery 1.8, la méthode
deferred.pipe()
est déconseillée. La méthodedeferred.then()
, qui la remplace, doit être utilisée à la place.
et
à partir de jQuery 1.8, la méthode
deferred.then()
renvoie une nouvelle promesse qui peut filtrer le statut et les valeurs d'un différé via une fonction, remplaçant la méthode désormais obsolètedeferred.pipe()
.
Les exemples ci-dessous pourraient encore être utiles à certains.
Ils servent à différentes fins:
.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é. Cela revient à 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 rappel à .pipe()
sera transmise comme argument aux rappels done
et 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 utilisez soit .then()
ou .pipe()
. Vous pourriez utiliser .pipe()
aux mêmes fins que .then()
mais l'inverse ne tient pas.
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 rappels 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" */
});
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 en quelque sorte les valeurs à l'avance afin de ne pas avoir à le faire dans les deux rappels individuellement? 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" */
});
De toute évidence, il s'agit d'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 le point.
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 à faire le deuxième appel dans un rappel done
:
$.ajax(...).done(function() {
// executed after first Ajax
$.ajax(...).done(function() {
// executed after second call
});
});
Supposons maintenant que vous vouliez découpler votre code et placer 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 autoriser un autre code qui appelle makeCalls
à attacher 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 souhaité car le deuxième appel est effectué à l'intérieur d'un callback done
et n'est pas accessible de l'extérieur.
La solution serait d'utiliser à la place .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
});
En utilisant .pipe()
, vous pouvez maintenant permettre d'ajouter des rappels à l'appel Ajax "interne" sans exposer le flux/l'ordre réel des appels.
En général, les objets différés fournissent un moyen intéressant de découpler votre code :)
Il n'y a aucun cas où vous DEVEZ utiliser then()
sur pipe()
. Vous pouvez toujours choisir d'ignorer la valeur que pipe()
passera. Il peut y avoir un léger impact sur les performances pour l'utilisation de pipe
- mais il est peu probable qu'il importe.
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 en a importance pour la valeur de retour. Si vous le jetez, vous violez cette construction sémantique.
C'est comme avoir une fonction qui renvoie une valeur qui n'est jamais utilisée: confuse.
Utilisez donc then()
quand vous devriez, et pipe()
quand vous devriez ...
En fait, il s'avère que la différence entre .then()
et .pipe()
a été jugée inutile et a été faite pour être la même que pour jQuery version 1.8.
De n commentaire de jaubourg
dans le suivi des bogues de jQuery ticket # 1101 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE/A":
En 1.8, nous supprimerons l'ancien et le remplacerons par le tube actuel. Mais la conséquence très triste est que nous devrons dire aux gens d'utiliser le non standard fait, échouer et progresser, car la proposition ne fournit pas simple, EFFICACE, signifie simplement ajouter un rappel.
(mine d'emphassis)