Qu'est-ce que l'anti-modèle de construction de promesse explicite et comment l'éviter?
J'écrivais du code qui fait quelque chose qui ressemble à:
function getStuffDone(param) { | function getStuffDone(param) {
var d = Q.defer(); /* or $q.defer */ | return new Promise(function(resolve, reject) {
// or = new $.Deferred() etc. | // using a promise constructor
myPromiseFn(param+1) | myPromiseFn(param+1)
.then(function(val) { /* or .done */ | .then(function(val) {
d.resolve(val); | resolve(val);
}).catch(function(err) { /* .fail */ | }).catch(function(err) {
d.reject(err); | reject(err);
}); | });
return d.promise; /* or promise() */ | });
} | }
Quelqu'un m'a dit que cela s'appelait respectivement "l' anti-modèle différé " ou "l' Promise
anti-modèle du constructeur ", qu'est-ce qu'il y a de mauvais dans ce code et pourquoi est-ce appelé un anti - modèle ?
Réponses
L' anti-modèle différé (maintenant anti-modèle de construction explicite) inventé par Esailija est un anti-modèle commun que les gens qui sont nouveaux dans les promesses font, je l'ai fait moi-même lorsque j'ai utilisé les promesses pour la première fois. Le problème avec le code ci-dessus est qu'il ne parvient pas à utiliser le fait que la chaîne promet.
Les promesses peuvent s'enchaîner .then
et vous pouvez retourner les promesses directement. Votre code dans getStuffDone
peut être réécrit comme:
function getStuffDone(param){
return myPromiseFn(param+1); // much nicer, right?
}
Les promesses consistent à rendre le code asynchrone plus lisible et à se comporter comme du code synchrone sans cacher ce fait. Les promesses représentent une abstraction sur une valeur d'opération ponctuelle, elles abstraits la notion d'énoncé ou d'expression dans un langage de programmation.
Vous ne devez utiliser des objets différés que lorsque vous convertissez une API en promesses et que vous ne pouvez pas le faire automatiquement, ou lorsque vous écrivez des fonctions d'agrégation qui s'expriment plus facilement de cette manière.
Citant Esailija:
C'est l'anti-pattern le plus courant. Il est facile de tomber là-dedans lorsque vous ne comprenez pas vraiment les promesses et que vous les considérez comme des émetteurs d'événements glorifiés ou un utilitaire de rappel. Récapitulons: les promesses consistent à faire en sorte que le code asynchrone conserve la plupart des propriétés perdues du code synchrone telles que l'indentation plate et un canal d'exception.
Qu'est ce qui ne va pas avec ça?
Mais le modèle fonctionne!
Quel chanceux êtes-vous. Malheureusement, ce n'est probablement pas le cas, car vous avez probablement oublié un cas de pointe. Dans plus de la moitié des occurrences que j'ai vues, l'auteur a oublié de s'occuper du gestionnaire d'erreurs:
return new Promise(function(resolve) {
getOtherPromise().then(function(result) {
resolve(result.property.example);
});
})
Si l'autre promesse est rejetée, cela se passera inaperçu au lieu d'être propagé à la nouvelle promesse (où elle serait gérée) - et la nouvelle promesse reste à jamais en suspens, ce qui peut induire des fuites.
La même chose se produit dans le cas où votre code de rappel provoque une erreur - par exemple, quand result
n'a pas de property
et qu'une exception est levée. Cela resterait sans réponse et laisserait la nouvelle promesse non résolue.
En revanche, l'utilisation .then()
prend automatiquement en charge ces deux scénarios et rejette la nouvelle promesse lorsqu'une erreur se produit:
return getOtherPromise().then(function(result) {
return result.property.example;
})
L'anti-modèle différé est non seulement encombrant, mais également sujet aux erreurs . L'utilisation .then()
pour le chaînage est beaucoup plus sûre.
Mais j'ai tout géré!
Vraiment? Bien. Cependant, ce sera assez détaillé et copieux, surtout si vous utilisez une bibliothèque de promesses qui prend en charge d'autres fonctionnalités telles que l'annulation ou la transmission de messages. Ou peut-être que ce sera le cas dans le futur, ou vous souhaitez échanger votre bibliothèque contre une meilleure? Vous ne voudrez pas réécrire votre code pour cela.
Les méthodes des bibliothèques ( then
) ne supportent pas seulement nativement toutes les fonctionnalités, elles peuvent également avoir certaines optimisations en place. Leur utilisation rendra probablement votre code plus rapide, ou du moins permettra d'être optimisé par les futures révisions de la bibliothèque.
Comment éviter cela?
Ainsi, chaque fois que vous vous trouvez en train de créer manuellement un Promise
ou Deferred
et que des promesses déjà existantes sont impliquées, vérifiez d'abord l'API de la bibliothèque . L'anti-modèle Différé est souvent appliqué par des personnes qui voient les promesses [uniquement] comme un modèle d'observateur - mais les promesses sont plus que des rappels : elles sont censées être composables. Chaque bibliothèque décente a beaucoup de fonctions faciles à utiliser pour la composition de promesses de toutes les manières imaginables, en prenant soin de toutes les choses de bas niveau que vous ne voulez pas traiter.
Si vous avez trouvé un besoin de rédiger des promesses d'une nouvelle manière qui n'est pas prise en charge par une fonction d'assistance existante, écrire votre propre fonction avec des différés inévitables devrait être votre dernière option. Envisagez de passer à une bibliothèque plus fonctionnelle et / ou de signaler un bogue sur votre bibliothèque actuelle. Son mainteneur doit être capable de dériver la composition à partir de fonctions existantes, d'implémenter une nouvelle fonction d'assistance pour vous et / ou d'aider à identifier les cas extrêmes à gérer.