Was ist das explizite Versprechenskonstruktions-Antimuster und wie vermeide ich es?

May 22 2014

Ich habe Code geschrieben, der etwas bewirkt, das so aussieht:

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() */ |     });
}                                        | }

Jemand hat mir gesagt, dass dies als " verzögertes Antimuster " bzw. " PromiseKonstruktor-Antimuster " bezeichnet wird. Was ist schlecht an diesem Code und warum wird dies als Antimuster bezeichnet ?

Antworten

380 BenjaminGruenbaum May 22 2014 at 17:07

Das von Esailija geprägte verzögerte Antimuster (jetzt explizit konstruiertes Anti-Muster) ist ein weit verbreitetes Anti-Muster-Volk, das neu in Versprechen ist. Ich habe es selbst gemacht, als ich Versprechen zum ersten Mal verwendet habe. Das Problem mit dem obigen Code ist, dass die Tatsache, die Kette verspricht, nicht ausgenutzt wird.

Versprechen können verkettet werden .thenund Sie können Versprechen direkt zurückgeben. Ihr Code in getStuffDonekann wie folgt umgeschrieben werden:

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

Bei Versprechungen geht es darum, asynchronen Code lesbarer zu machen und sich wie synchroner Code zu verhalten, ohne diese Tatsache zu verbergen. Versprechen stellen eine Abstraktion über einen Wert einer einmaligen Operation dar, sie abstrahieren den Begriff einer Aussage oder eines Ausdrucks in einer Programmiersprache.

Sie sollten verzögerte Objekte nur verwenden, wenn Sie eine API in Versprechen konvertieren und dies nicht automatisch tun können oder wenn Sie Aggregationsfunktionen schreiben, die auf diese Weise einfacher ausgedrückt werden.

Zitat Esailija:

Dies ist das häufigste Anti-Muster. Es ist leicht, darauf hereinzufallen, wenn Sie Versprechen nicht wirklich verstehen und sie als verherrlichte Ereignisemitter oder Rückrufdienstprogramm betrachten. Fassen wir noch einmal zusammen: Bei Versprechungen geht es darum, dass asynchroner Code die meisten verlorenen Eigenschaften von synchronem Code wie flache Einrückungen und einen Ausnahmekanal beibehält.

142 Bergi Aug 29 2014 at 20:28

Was stimmt damit nicht?

Aber das Muster funktioniert!

Du Glückspilz. Leider ist dies wahrscheinlich nicht der Fall, da Sie wahrscheinlich einen Randfall vergessen haben. In mehr als der Hälfte der Fälle, die ich gesehen habe, hat der Autor vergessen, sich um den Fehlerbehandler zu kümmern:

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

Wenn das andere Versprechen abgelehnt wird, geschieht dies unbemerkt, anstatt an das neue Versprechen weitergegeben zu werden (wo es behandelt wird) - und das neue Versprechen bleibt für immer ausstehend, was zu Undichtigkeiten führen kann.

Dasselbe passiert, wenn Ihr Rückrufcode einen Fehler verursacht - z. B. wenn resultkeine vorhanden ist propertyund eine Ausnahme ausgelöst wird. Das würde unbehandelt bleiben und das neue Versprechen ungelöst lassen.

Im Gegensatz dazu .then()kümmert sich die Verwendung automatisch um beide Szenarien und lehnt das neue Versprechen ab, wenn ein Fehler auftritt:

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

Das verzögerte Antimuster ist nicht nur umständlich, sondern auch fehleranfällig . Die Verwendung .then()zur Verkettung ist viel sicherer.

Aber ich habe alles erledigt!

"Ja wirklich?" Gut. Dies ist jedoch sehr detailliert und umfangreich, insbesondere wenn Sie eine Versprechensbibliothek verwenden, die andere Funktionen wie Stornierung oder Nachrichtenübermittlung unterstützt. Oder vielleicht wird es in Zukunft so sein, oder Sie möchten Ihre Bibliothek gegen eine bessere austauschen? Sie möchten Ihren Code dafür nicht neu schreiben.

Die Methoden ( then) der Bibliotheken unterstützen nicht nur alle Funktionen von Haus aus, sondern verfügen möglicherweise auch über bestimmte Optimierungen. Wenn Sie sie verwenden, wird Ihr Code wahrscheinlich schneller oder kann zumindest durch zukünftige Überarbeitungen der Bibliothek optimiert werden.

Wie vermeide ich das?

Wenn Sie also manuell ein Promiseoder Deferredbereits vorhandene Versprechen erstellen , überprüfen Sie zuerst die Bibliotheks-API . Das verzögerte Antimuster wird oft von Menschen angewendet, die Versprechen [nur] als Beobachtermuster betrachten - aber Versprechen sind mehr als Rückrufe : Sie sollen zusammensetzbar sein. Jede anständige Bibliothek verfügt über viele benutzerfreundliche Funktionen für die Zusammenstellung von Versprechungen auf jede denkbare Weise, die sich um all die einfachen Dinge kümmern, mit denen Sie sich nicht befassen möchten.

Wenn Sie festgestellt haben, dass Sie einige Versprechen auf eine neue Art und Weise verfassen müssen, die von einer vorhandenen Hilfsfunktion nicht unterstützt wird, sollte das Schreiben einer eigenen Funktion mit unvermeidbaren Verzögerungen Ihre letzte Option sein. Wechseln Sie zu einer funktionsfähigeren Bibliothek und / oder melden Sie einen Fehler in Ihrer aktuellen Bibliothek. Der Betreuer sollte in der Lage sein, die Zusammensetzung aus vorhandenen Funktionen abzuleiten, eine neue Hilfsfunktion für Sie zu implementieren und / oder dabei zu helfen, die zu behandelnden Randfälle zu identifizieren.