Das Konzept der "Versprechen" hat die Art und Weise verändert, in der wir asynchrones JavaScript schreiben. Im vergangenen Jahr haben viele Frameworks eine Form des Promise-Musters integriert, um das Schreiben, Lesen und Verwalten von asynchronem Code zu erleichtern. Zum Beispiel fügte jQuery $ .Deferred () hinzu und NodeJS verfügt über die Q- und jspromise-Module, die sowohl auf dem Client als auch auf dem Server funktionieren. Clientseitige MVC-Frameworks wie EmberJS und AngularJS implementieren auch eigene Versionen von Promises.
Aber es muss nicht aufhören: Wir können ältere Lösungen überdenken und Versprechen auf sie anwenden. In diesem Artikel machen wir genau das: Überprüfen Sie ein Formular mithilfe des Promise-Musters, um eine sehr einfache API bereitzustellen.
Versprechen benachrichtigen das Ergebnis einer Operation.
Vereinfacht gesagt, Versprechen informieren das Ergebnis einer Operation. Das Ergebnis kann ein Erfolg oder ein Misserfolg sein, und die Operation selbst kann alles sein, was einem einfachen Vertrag entspricht. Ich entschied mich für das Wort Vertrag weil Sie diesen Vertrag auf verschiedene Arten gestalten können. Zum Glück kam die Entwicklungsgemeinschaft zu einem Konsens und erstellte eine Spezifikation mit dem Namen Promises / A+.
Nur die Operation weiß wirklich, wann sie abgeschlossen ist. Als solche ist es für die Benachrichtigung des Ergebnisses über den Promises / A + -Vertrag verantwortlich. Mit anderen Worten, es Versprechen um Ihnen das Endergebnis nach Fertigstellung mitzuteilen.
Die Operation gibt einen zurück versprechen
Objekt, und Sie können Ihre Rückrufe mit dem anhängen erledigt()
oder Scheitern()
Methoden. Die Operation kann ihr Ergebnis durch Aufruf melden promise.resolve ()
oder promise.reject ()
, beziehungsweise. Dies ist in der folgenden Abbildung dargestellt:
Lassen Sie mich ein plausibles Szenario zeichnen.
Wir können ältere Lösungen überdenken und Versprechen auf sie anwenden.
Die clientseitige Formularvalidierung beginnt immer mit den einfachsten Absichten. Möglicherweise haben Sie ein Anmeldeformular mit Name und Email Sie müssen sicherstellen, dass der Benutzer für beide Felder gültige Eingaben vornimmt. Das scheint ziemlich unkompliziert, und Sie beginnen mit der Implementierung Ihrer Lösung.
Ihnen wird dann mitgeteilt, dass E-Mail-Adressen eindeutig sein müssen, und Sie beschließen, die E-Mail-Adresse auf dem Server zu bestätigen. Der Benutzer klickt also auf die Schaltfläche zum Senden, der Server überprüft die Eindeutigkeit der E-Mail und die Seite wird aktualisiert, um Fehler anzuzeigen. Das scheint der richtige Ansatz zu sein, richtig? Nee. Ihr Kunde möchte eine glatte Benutzererfahrung. Besucher sollten Fehlermeldungen sehen, ohne die Seite zu aktualisieren.
Ihr Formular hat die Name Feld, das keine serverseitige Unterstützung erfordert, aber dann haben Sie die Email Feld, in dem Sie eine Anforderung an den Server stellen müssen. Serveranfragen bedeutet $ .ajax ()
Anrufe, so dass Sie die E-Mail-Bestätigung in Ihrer Rückruffunktion durchführen müssen. Wenn Ihr Formular über mehrere Felder verfügt, für die eine serverseitige Unterstützung erforderlich ist, handelt es sich bei Ihrem Code um ein verschachteltes Durcheinander $ .ajax ()
Anrufe in Rückrufen. Rückrufe in Rückrufen: "Willkommen in der Rückruf Hölle! Wir hoffen, Sie haben einen miserablen Aufenthalt!".
Wie gehen wir also mit der Callback-Hölle um??
Machen Sie einen Schritt zurück und denken Sie über dieses Problem nach. Wir haben eine Reihe von Operationen, die entweder erfolgreich sein oder fehlschlagen können. Jedes dieser Ergebnisse kann als a erfasst werden Versprechen
, Die Operationen können von einfachen clientseitigen Prüfungen bis zu komplexen serverseitigen Überprüfungen reichen. Versprechungen bieten Ihnen außerdem den zusätzlichen Vorteil der Konsistenz und lassen Sie die Prüfung der Art der Validierung unter bestimmten Bedingungen vermeiden. Mal sehen, wie wir das schaffen können.
Wie ich bereits erwähnt habe, gibt es mehrere vielversprechende Implementierungen, aber ich werde mich auf die $ .Deferred () Promise-Implementierung von jQuery konzentrieren.
Wir werden ein einfaches Validierungs-Framework erstellen, bei dem jede Prüfung sofort entweder ein Ergebnis oder ein Versprechen zurückgibt. Als Benutzer dieses Frameworks müssen Sie sich nur eines merken: "Es gibt immer ein Versprechen". Lass uns anfangen.
Ich denke, es ist einfacher, die Einfachheit der Versprechen aus Sicht des Verbrauchers zu erkennen. Nehmen wir an, ich habe ein Formular mit drei Feldern: Name, E-Mail und Adresse:
Ich werde zunächst die Validierungskriterien mit dem folgenden Objekt konfigurieren. Dies dient auch als API unseres Frameworks:
var validationConfig = '.name': Überprüft: 'Erforderlich', Feld: 'Name', '.email': Überprüft: ['Erforderlich'], Feld: 'E-Mail', '.Adresse': prüft: ['zufällig', 'erforderlich'], Feld: 'Adresse';
Die Schlüssel dieses Konfigurationsobjekts sind jQuery-Selektoren. Ihre Werte sind Objekte mit den folgenden zwei Eigenschaften:
prüft
: eine Zeichenfolge oder ein Array von Validierungen.Feld
: Der vom Menschen lesbare Feldname, der zum Melden von Fehlern für dieses Feld verwendet wirdWir können unseren Prüfer nennen, der als globale Variable verfügbar gemacht wird V
, so was:
V.validate (validationConfig) .done (function () // Success) .fail (function (errors) // Überprüfung fehlgeschlagen. Fehler hat die Details);
Beachten Sie die Verwendung der erledigt()
und Scheitern()
Rückrufe; Dies sind die Standard-Rückrufe für die Übergabe eines Promise-Ergebnisses. Wenn wir weitere Formularfelder hinzufügen, können Sie das einfach ergänzen validationConfig
ohne den Rest des Setups zu stören (das Open-Closed-Prinzip in Aktion). Tatsächlich können wir andere Validierungen hinzufügen, wie die Eindeutigkeitsbeschränkung für E-Mail-Adressen, indem wir das Validator-Framework erweitern (was wir später sehen werden)..
Das ist also die verbraucherorientierte API für das Validator-Framework. Lassen Sie uns jetzt eintauchen und sehen, wie es unter der Haube funktioniert.
Der Prüfer wird als Objekt mit zwei Eigenschaften verfügbar gemacht:
Art
: enthält die verschiedenen Arten von Validierungen und dient auch als Erweiterungspunkt für das Hinzufügen weiterer.bestätigen
: Die Kernmethode, die die Validierungen basierend auf dem bereitgestellten Konfigurationsobjekt durchführt.Die Gesamtstruktur kann wie folgt zusammengefasst werden:
var V = (Funktion ($) Var-Validator = / * * Erweiterungspunkt - fügen Sie einfach diesen Hash hinzu * * V.type ['my-validator'] = * ok: function (value) return true; , * message: 'Fehlermeldung für meinen Validator' * * / type: 'required': ok: function (value) // ist gültig?, message: 'Dieses Feld ist erforderlich',… , / ** * * @param config * * '': string | Objekt | [string] * * / validate: function (config) // 1. Normalisieren Sie das Konfigurationsobjekt // 2. Konvertieren Sie jede Validierung in ein Versprechen // // Umbrechen in ein Master-Versprechen // 4. Rückgabe des Master-Versprechens ; ) (jQuery);
Das bestätigen
Methode liefert die Grundlagen dieses Rahmens. Wie in den obigen Kommentaren zu sehen ist, gibt es hier vier Schritte:
1. Normalisieren Sie das Konfigurationsobjekt.
Hier gehen wir durch unser Konfigurationsobjekt und konvertieren es in eine interne Darstellung. Dabei werden hauptsächlich alle Informationen erfasst, die zur Validierung erforderlich sind, und wenn nötig Fehler gemeldet:
Funktion normalizeConfig (config) config = config || ; var validations = []; $ .each (config, function (selector, obj) // Array für vereinfachte Überprüfung erstellen var checks = $ .isArray (obj.checks)? obj.checks: [obj.checks]; $ .each (prüft, Funktion (idx, check) validations.push (control: $ (Selector), check: getValidator (check), checkName: check, Feld: obj.field););); Validierungen zurückgeben; Funktion getValidator (type) if ($ .type (type) === 'string' && validator.type [type]) gibt validator.type [type] zurück; return validator.noCheck;
Dieser Code durchläuft die Schlüssel im Konfigurationsobjekt und erstellt eine interne Darstellung der Validierung. Wir werden diese Darstellung in der verwenden bestätigen
Methode.
Das getValidator ()
Helfer holt das Prüferobjekt aus der Art
hash Wenn wir keinen finden, geben wir den zurück noCheck
Validator, der immer true zurückgibt.
2. Wandeln Sie jede Validierung in ein Versprechen um.
Hier stellen wir sicher, dass jede Validierung ein Versprechen ist, indem der Rückgabewert von überprüft wird validation.ok ()
. Wenn es das enthält dann()
Methode, wir wissen, dass es ein Versprechen ist (dies ist gemäß der Versprechen / A + -Spezifikation). Wenn nicht, erstellen wir ein Ad-hoc-Versprechen, das je nach Rückgabewert aufgelöst oder abgelehnt wird.
validate: function (config) // 1. Normalisieren Sie das Konfigurationsobjekt config = normalizeConfig (config); var verspricht = [], prüft = []; // 2. Konvertieren Sie jede Validierung in ein Versprechen $ .each (config, function (idx, v) var value = v.control.val (); var retVal = v.check.ok (value); // Machen Sie ein versprechen, überprüfen basiert auf Versprechen / A + spez. if (retVal.then) promises.push (retVal); else var p = $ .Deferred (); if (retVal) p.resolve (); else p.reject (); promises.push (p.promise ()); checks.push (v);); // 3. Ein Master-Versprechen einpacken // 4. Das Master-Versprechen zurücksenden
3. Einpacken in ein Master-Versprechen.
Im vorigen Schritt haben wir eine Reihe von Versprechungen erstellt. Wenn alle erfolgreich sind, möchten wir entweder einmal auflösen oder mit detaillierten Fehlerinformationen ausfallen. Wir können dies tun, indem wir alle Versprechen in ein einziges Versprechen verpacken und das Ergebnis verbreiten. Wenn alles gut geht, entscheiden wir uns einfach für das Master-Versprechen.
Bei Fehlern können wir aus unserer internen Validierungsrepräsentation lesen und diese für das Reporting verwenden. Da es mehrere Validierungsfehler geben kann, durchlaufen wir die Versprechen
Array und lesen Sie die Zustand()
Ergebnis. Wir sammeln alle abgelehnten Versprechen in die gescheitert
Array und Anruf ablehnen()
auf dem Meisterversprechen:
// 3. In ein Master-Versprechen einbetten var masterPromise = $ .Deferred (); $ .when.apply (null, verspricht) .done (function () masterPromise.resolve ();) .fail (function () var failed = []; $ .each (verspricht, Funktion (idx, x) if (x.state () === 'Abgelehnt') var failedCheck = prüft [idx]; var error = check: failedCheck.checkName, Fehler: failedCheck.check.message, Feld: failedCheck.field, Steuerelement: failedCheck.control; failed.push (error);;; masterPromise.reject (failed);); // 4. Rückgabe des Master-Versprechens return masterPromise.promise ();
4. Schicken Sie das Master-Versprechen zurück.
Zum Schluss kehren wir das Master-Versprechen vom zurück bestätigen()
Methode. Dies ist das Versprechen, auf das der Client-Code einrichtet erledigt()
und Scheitern()
Rückrufe.
Die Schritte zwei und drei sind der Kern dieses Rahmens. Durch die Normalisierung der Validierungen in ein Versprechen können wir sie einheitlich behandeln. Wir haben mehr Kontrolle über ein Master-Promise-Objekt und können zusätzliche Kontextinformationen anfügen, die für den Endbenutzer nützlich sein können.
In der Demo-Datei finden Sie eine vollständige Verwendung des Validator-Frameworks. Wir nehmen das erledigt()
Rückruf, um Erfolg zu melden und Scheitern()
um eine Liste der Fehler für jedes der Felder anzuzeigen. Die folgenden Screenshots zeigen die Erfolge und Misserfolge:
Die Demo verwendet dieselbe HTML- und Validierungskonfiguration, die zuvor in diesem Artikel erwähnt wurde. Der einzige Zusatz ist der Code, der die Alarme anzeigt. Beachten Sie die Verwendung der erledigt()
und Scheitern()
Rückrufe, um die Validierungsergebnisse zu verarbeiten.
Funktion showAlerts (Fehler) var alertContainer = $ ('. alert'); $ ('. error'). remove (); if (! fehler) alertContainer.html ('Alle bestanden'); else $ .each (Fehler, Funktion (idx, err) var msg = $ ('') .addClass (' error ') .text (err.error); err.control.parent (). append (msg); ); $ ('. validate'). click (Funktion () $ ('.anzeiger'). show (); $ ('. alert'). empty (); V.validate (validationConfig) .done (Funktion () $ ('. Anzeiger'). hide (); showAlerts ();) .fail (Funktion (Fehler) $ ('. Anzeiger'). hide (); showAlerts (Fehler);); );
Ich habe bereits erwähnt, dass wir dem Framework durch die Erweiterung des Validators weitere Validierungsoperationen hinzufügen können Art
hash Bedenke die zufällig
Validator als Beispiel. Dieser Prüfer ist zufällig erfolgreich oder schlägt fehl. Ich weiß, dass es kein nützlicher Validator ist, aber es lohnt sich, einige seiner Konzepte zu beachten:
setTimeout ()
um die Validierung asynchron zu machen. Sie können dies auch als Simulation der Netzwerklatenz verstehen.OK()
Methode.// Mit einem Zufallsvalidator erweitern V.type ['random'] = ok: function (value) var verzögert = $ .Deferred (); setTimeout (function () var Ergebnis = Math.random () < 0.5; if (result) deferred.resolve(); else deferred.reject(); , 1000); return deferred.promise(); , message: 'Failed randomly. No hard feelings.' ;
In der Demo habe ich diese Validierung auf der verwendet Adresse Feld wie folgt:
var validationConfig = / * wurde aus Gründen der Kürze ausgewählt * / '.address': prüft: ['random', 'required'], Feld: 'Address';
Ich hoffe, dass Ihnen dieser Artikel eine gute Vorstellung davon gibt, wie Sie Versprechen auf alte Probleme anwenden und einen eigenen Rahmen schaffen können. Der auf Promise basierende Ansatz ist eine fantastische Lösung für abstrakte Operationen, die synchron ausgeführt werden können oder nicht. Sie können auch Rückrufe verketten und sogar Versprechen höherer Ordnung aus einer Reihe anderer Versprechen zusammenstellen.
Das Promise-Muster ist in einer Vielzahl von Szenarien anwendbar, und Sie werden hoffentlich auf einige davon stoßen und eine sofortige Übereinstimmung feststellen!