web-dev-qa-db-fra.com

Qu'est-ce que l'antipattern de construction de promesse explicite et comment puis-je 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'appelle le " anti-modèle différé " ou le "Promise constructeur du modèle antipattern ", qu'est-ce qui ne va pas avec ce code et pourquoi s'appelle-t-il un antipattern ?

459

Le 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 quand J'ai d'abord utilisé des promesses. Le problème avec le code ci-dessus est qu'il n'utilise pas le fait que la chaîne de promesses.

Les promesses peuvent être chaînées avec .then et vous pouvez les retourner directement. Votre code dans getStuffDone peut être réécrit comme suit:

function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

Les promesses visent à rendre le code asynchrone plus lisible et se comportent comme un code synchrone sans le cacher. Les promesses représentent une abstraction d'une valeur d'opération unique, elles abstiennent la notion d'instruction ou d'expression dans un langage de programmation.

Vous ne devez utiliser des objets différés que lorsque vous êtes conversion d'une API en promesses et que vous ne pouvez pas le faire automatiquement, ou lorsque vous écrivez des fonctions d'agrégation plus faciles à exprimer.

Citant Esailija:

C'est l'anti-pattern le plus commun. Il est facile de tomber dans cela lorsque vous ne comprenez pas vraiment les promesses et que vous les considérez comme des émetteurs d'événements glorifiés ou des utilitaires 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 à plat et un canal d'exception.

320

Qu'est ce qui ne va pas avec ça?

Mais le motif fonctionne!

Quel chanceux êtes-vous. Malheureusement, ce n'est probablement pas le cas, car vous avez probablement oublié une affaire Edge. Dans plus de la moitié des cas que j'ai vus, 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 produira inaperçu au lieu d'être transmis à la nouvelle promesse (où elle serait gérée) - et la nouvelle promesse reste toujours en attente, ce qui peut provoquer 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 non traité et laisserait la nouvelle promesse non résolue.

En revanche, l'utilisation de .then() résout automatiquement ces deux scénarios et rejette la nouvelle promesse lorsqu'une erreur se produit:

 return getOtherPromise().then(function(result) {
     return result.property.example;
 })

L'antipattern différé est non seulement encombrant, mais aussi sujet aux erreurs . Utiliser .then() pour chaîner est beaucoup plus sûr.

Mais j'ai tout manipulé!

Vraiment? Bien. Toutefois, cela 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 à l'avenir, 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 prennent pas en charge toutes les fonctionnalités de manière native, elles peuvent également avoir certaines optimisations en place. Leur utilisation rendra probablement votre code plus rapide, ou du moins permettra d’être optimisée lors de futures révisions de la bibliothèque.

Comment puis-je l'éviter?

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'antipattern différé est souvent appliqué par les personnes qui voient les promesses [uniquement] comme un motif d'observation - mais les promesses sont plus que de rappels : elles sont supposées être composable. Chaque bonne bibliothèque possède de nombreuses fonctions faciles à utiliser pour composer les promesses de toutes les manières imaginables, en prenant soin de tous les éléments de bas niveau que vous ne voulez pas traiter.

Si vous avez besoin de composer certaines promesses d’une manière nouvelle qui n’est pas prise en charge par une fonction d’assistant 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 créer un bogue contre votre bibliothèque actuelle. Son responsable doit être en mesure de dériver la composition à partir de fonctions existantes, de mettre en place une nouvelle fonction d'assistance et/ou d'identifier les cas Edge à traiter.

120
Bergi