Was ist das Antimuster der expliziten Versprechungskonstruktion und wie vermeide ich es?

Lesezeit: 9 Minuten

Was ist das Antimuster der expliziten Versprechungskonstruktion und wie vermeide
Benjamin Grünbaum

Ich habe Code geschrieben, der etwas tut, 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 sagte mir, das heißt “verzögertes Antimuster” oder der “Promise Konstruktor Antimuster” bzw. was ist schlecht an diesem Code und warum heißt er an Antimuster?

  • oder hat die catch blockieren im getStuffDone Wrapper das Antimuster?

    – Der Dembinski

    6. Januar 2017 um 23:45 Uhr

  • Zumindest für die Einheimischen Promise Beispiel haben Sie auch unnötige Funktionswrapper für die .then und .catch Handler (dh es könnte einfach sein .then(resolve).catch(reject).) Ein perfekter Sturm von Anti-Patterns.

    – Noah Freitas

    30. August 2017 um 17:48 Uhr


  • @NoahFreitas dieser Code ist aus didaktischen Gründen so geschrieben. Ich habe diese Frage und Antwort geschrieben, um Leuten zu helfen, die auf dieses Problem stoßen, nachdem sie viel Code gelesen haben, der so aussieht 🙂

    – Benjamin Grünbaum

    30. August 2017 um 18:28 Uhr

  • Siehe auch stackoverflow.com/questions/57661537/… um zu erfahren, wie man nicht nur die explizite Promise-Konstruktion, sondern auch die Verwendung einer globalen Variablen eliminiert.

    – David Spector

    26. August 2019 um 21:01 Uhr

  • Was hat es mit den ungeraden Side-by-Side-Codebeispielen auf sich? Habe das hier noch nie gesehen, finde es ziemlich verwirrend. Musste den Revisionsverlauf überprüfen, um zu verstehen, dass beide Beispiele für dasselbe sind.

    – Alex

    5. August 2021 um 10:11 Uhr

Was ist das Antimuster der expliziten Versprechungskonstruktion und wie vermeide
Benjamin Grünbaum

Die verzögertes Antimuster (jetzt explizit konstruiertes Antimuster) Geprägt von Esailija ist ein gängiges Anti-Muster, das Leute machen, die noch keine Erfahrung mit Versprechungen haben. Ich habe es selbst gemacht, als ich zum ersten Mal Versprechen verwendet habe. Das Problem mit dem obigen Code besteht darin, dass er die Tatsache nicht nutzt, dass die Kette verspricht.

Versprechen können mit verkettet werden .then und Sie können Versprechen direkt zurückgeben. Dein Code drin getStuffDone kann umgeschrieben werden als:

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

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

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

Zitat von Esailija:

Dies ist das häufigste Anti-Pattern. Es ist leicht, darauf hereinzufallen, wenn Sie Promises nicht wirklich verstehen und sie als glorifizierte Event-Emitter oder Callback-Utility betrachten. Fassen wir noch einmal zusammen: Bei Versprechungen geht es darum, dass asynchroner Code die meisten der verlorenen Eigenschaften von synchronem Code wie flache Einrückung und einen Ausnahmekanal beibehält.

  • @BenjaminGruenbaum: Ich bin zuversichtlich, dass ich dafür Verzögerungen verwende, daher ist keine neue Frage erforderlich. Ich dachte nur, es wäre ein Anwendungsfall, den Sie in Ihrer Antwort vermisst hätten. Was ich tue, scheint eher das Gegenteil von Aggregation zu sein, nicht wahr?

    – mhelvens

    25. September 2014 um 19:43 Uhr

  • @mhelvens Wenn Sie eine Nicht-Callback-API manuell in eine Promise-API aufteilen, die zum Teil „Konvertieren einer Callback-API in Promises“ passt. Beim Antimuster geht es darum, ein Versprechen ohne triftigen Grund in ein anderes Versprechen zu verpacken, Sie verpacken kein Versprechen, damit es hier nicht zutrifft.

    – Benjamin Grünbaum

    25. September 2014 um 19:47 Uhr

  • @BenjaminGruenbaum: Ah, ich dachte, dass Verzögerungen selbst als Anti-Pattern angesehen wurden, was damit zusammenhängt, dass Bluebird sie missbilligt, und Sie erwähnen „Konvertieren einer API in Versprechen“ (was auch ein Fall ist, in dem zunächst kein Versprechen eingepackt wird).

    – mhelvens

    25. September 2014 um 20:05 Uhr

  • @mhelvens Ich denke das Überschuss Deferred Anti Pattern wäre genauer für das, was es tatsächlich tut. Bluebird hat die .defer() api in den neueren (und werfen sicheren) Promise-Konstruktor, hat es (in keiner Weise) die Idee des Erstellens von Promises abgelehnt 🙂

    – Benjamin Grünbaum

    25. September 2014 um 20:06 Uhr

  • Danke @Roamer-1888, deine Referenz hat mir geholfen, endlich herauszufinden, was mein Problem war. Sieht so aus, als hätte ich verschachtelte (nicht zurückgegebene) Versprechen erstellt, ohne es zu merken.

    – ghuroo

    30. Mai 2017 um 1:21 Uhr


Was ist das Antimuster der expliziten Versprechungskonstruktion und wie vermeide
Bergi

Was stimmt damit nicht?

Aber das Muster funktioniert!

Du Glückspilz. Leider ist dies wahrscheinlich nicht der Fall, da Sie wahrscheinlich einen Grenzfall vergessen haben. In mehr als der Hälfte der Fälle, die ich gesehen habe, hat der Autor vergessen, sich um die Fehlerbehandlung 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 würde) – und das neue Versprechen bleibt für immer in der Schwebe, was zu Lecks führen kann.

Dasselbe passiert, wenn Ihr Callback-Code einen Fehler verursacht – zB when result hat kein property und eine Ausnahme wird geworfen. Das würde unbehandelt bleiben und das neue Versprechen ungelöst lassen.

Im Gegensatz dazu verwenden .then() kümmert sich automatisch um diese beiden 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. Verwenden .then() zum Verketten ist viel sicherer.

Aber ich habe alles erledigt!

Wirklich? Gut. Dies wird jedoch ziemlich detailliert und umfangreich sein, insbesondere wenn Sie eine Promise-Bibliothek verwenden, die andere Funktionen wie Stornierung oder Nachrichtenübermittlung unterstützt. Oder vielleicht doch in Zukunft, oder Sie möchten Ihre Bibliothek gegen eine bessere tauschen? Sie werden Ihren Code dafür nicht umschreiben wollen.

Die Methoden der Bibliotheken (then) unterstützen nicht nur alle Funktionen nativ, 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 es?

Wann immer Sie also feststellen, dass Sie manuell eine erstellen Promise oder Deferred und bereits bestehende Versprechungen involviert sind, Überprüfen Sie zuerst die Bibliotheks-API. Das verzögerte Antimuster wird oft von Leuten angewendet, die Versprechungen sehen [only] als Beobachter Muster – aber Versprechungen sind mehr als Rückrufe: Sie sollen zusammensetzbar sein. Jede anständige Bibliothek hat viele einfach zu bedienende Funktionen zum Verfassen von Versprechen auf jede erdenkliche Weise und kümmert sich um all die Dinge auf niedriger Ebene, mit denen Sie sich nicht befassen möchten.

Wenn Sie festgestellt haben, dass einige Promises auf eine neue Weise erstellt werden müssen, die von einer vorhandenen Hilfsfunktion nicht unterstützt wird, sollte das Schreiben Ihrer eigenen Funktion mit unvermeidlichen Verzögerungen Ihre letzte Option sein. Erwägen Sie, zu einer Bibliothek mit mehr Funktionen zu wechseln, und/oder melden Sie einen Fehler in Ihrer aktuellen Bibliothek. Ihr Betreuer sollte in der Lage sein, die Zusammensetzung aus bestehenden Funktionen abzuleiten, eine neue Hilfsfunktion für Sie zu implementieren und/oder dabei zu helfen, die Grenzfälle zu identifizieren, die behandelt werden müssen.

  • Gibt es Beispiele, außer einer Funktion einschließlich setTimeout wo der Konstruktor verwendet werden konnte, aber nicht als “Promise Konstruktor Antipattern” betrachtet werden konnte?

    – Gast271314

    23. November 2015 um 21:39 Uhr

  • @guest271314: Alles asynchron, das kein Versprechen zurückgibt. Oft genug erzielen Sie jedoch mit den speziellen Versprechungshelfern der Bibliotheken bessere Ergebnisse. Und stellen Sie sicher, dass Sie immer auf der niedrigsten Ebene versprechen, damit es nicht “eine Funktion inkl setTimeout“, aber “die Funktion setTimeout selbst“.

    – Bergi

    23. November 2015 um 22:13 Uhr


  • @guest271314 Eine Funktion, die nur einen Aufruf von enthält setTimeout unterscheidet sich deutlich von die Funktion setTimeout selbstnicht wahr?

    – Bergi

    23. November 2015 um 22:21 Uhr


  • Vielleicht ist mir hier kein klarer Unterschied bewusst. function () {setTimeout(dostuff, duration)} , setTimeout(dostuff, duration) ? Kann Kontextunterschiede zwischen zwei beschreiben, wenn die obigen zwei Variationen eine genaue Darstellung von sind oder nicht “Aufruf von setTimeout unterscheidet sich deutlich von der Funktion setTimeout” ?

    – Gast271314

    23. November 2015 um 22:26 Uhr


  • Ich denke, eine der wichtigen Lektionen hier, die bisher nicht klar gesagt wurde, ist, dass ein Promise und sein verkettetes „dann“ eine asynchrone Operation darstellen: Die anfängliche Operation befindet sich im Promise-Konstruktor und der endgültige Endpunkt befindet sich im „ dann’ Funktion. Wenn Sie also eine Synchronisierungsoperation gefolgt von einer Asynchronisierungsoperation haben, fügen Sie das Synchronisierungsmaterial in das Promise ein. Wenn Sie einen asynchronen Vorgang gefolgt von einer Synchronisierung haben, fügen Sie das Synchronisierungsmaterial in das „dann“ ein. Geben Sie im ersten Fall das ursprüngliche Versprechen zurück. Geben Sie im zweiten Fall die Versprechen/dann-Kette zurück (die ebenfalls ein Versprechen ist).

    – David Spector

    27. August 2019 um 0:44 Uhr


1646900114 289 Was ist das Antimuster der expliziten Versprechungskonstruktion und wie vermeide
Jonas Wilms

Jetzt, 7 Jahre später, gibt es eine einfachere Antwort auf diese Frage:

Wie vermeide ich das explizite Konstruktor-Antimuster?

Verwenden async functions, dann await jedes Versprechen!

Anstatt manuell verschachtelte Promise-Ketten wie diese zu erstellen:

function promised() {
  return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
      getAnotherPromise(result).then(function(result2) {
        resolve(result2);
      });
    });
  });
}

drehen Sie einfach Ihre Funktion async und benutze die await Stichwort zu Ausführung der Funktion stoppen bis das Versprechen eingelöst wird:

async function promised() {
   const result =  await getOtherPromise();
   const result2 = await getAnotherPromise(result);
   return result2;
}

Dies hat verschiedene Vorteile:

  • Aufruf der async Die Funktion gibt immer ein Promise zurück, das mit dem zurückgegebenen Wert aufgelöst und abgelehnt wird, wenn ein Fehler in der asynchronen Funktion ausgelöst wird
  • Wenn ein awaited Promise ablehnt, wird der Fehler in die async-Funktion geworfen, also können Sie einfach try { ... } catch(error) { ... } es mag die synchronen Fehler.
  • Du kannst await innerhalb von Schleifen und if-Verzweigungen, wodurch der größte Teil der Logik der Promise-Kette trivial wird
  • Obwohl sich asynchrone Funktionen größtenteils wie Versprechensketten verhalten, sind sie viel einfacher zu lesen (und einfacher zu begründen).

Wie kann ich await ein Rückruf?

Wenn der Rückruf nur einmal zurückruft und die von Ihnen aufgerufene API noch kein Promise bereitstellt (die meisten tun dies!), ist dies der Fall einziger Grund So verwenden Sie einen Promise-Konstruktor:

 // Create a wrapper around the "old" function taking a callback, passing the 'resolve' function as callback
 const delay = time => new Promise((resolve, reject) =>
   setTimeout(resolve, time)
 ); 

 await delay(1000);

Wenn await stoppt die Ausführung, ruft an async function das Ergebnis direkt zurückgeben?

Nein. Wenn Sie eine asynchrone Funktion aufrufen, wird immer ein Promise zurückgegeben. Sie können dann await dieses Versprechen auch innerhalb einer asynchronen Funktion. Sie können nicht innerhalb einer synchronen Funktion auf das Ergebnis warten (Sie müssten aufrufen .then und Rückruf anhängen).

Konzeptionell synchron functions laufen immer in einem Job zu Ende, während async functions laufen synchron, bis sie ein erreichen awaitdann machen sie in einem anderen Job weiter.

  • Der springende Punkt bei der Verwendung einer asynchronen Funktion ist also Sie nicht Ausführung stoppen….

    – John Ktejik

    21. Juni 2021 um 14:47 Uhr

  • @john es gibt einen großen unterschied zwischen “stoppt die ausführung der async-funktion” und “stoppt die ausführung”.

    – Jonas Wilms

    21. Juni 2021 um 16:16 Uhr

  • Noch besser: Verwenden Sie async/await und fügen Sie eslint-Regeln hinzu no-floating-promises und no-misused-promises. Wenn Sie es vergessen await a Promise, der Linter wird dich anschreien.

    – Kodierer

    22. Oktober 2021 um 15:27 Uhr

987050cookie-checkWas ist das Antimuster der expliziten Versprechungskonstruktion und wie vermeide ich es?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy