Versprechen haben einen Zustand, sie beginnen als ausstehend und können sich auf folgendes einstellen:
- erfüllt bedeutet, dass die Berechnung erfolgreich abgeschlossen wurde.
- abgelehnt bedeutet, dass die Berechnung fehlgeschlagen ist.
Versprechen zurückgebende Funktionen sollten nie Ausnahmen auslösen, stattdessen sollten sie Ablehnungen zurückgeben. Das Werfen aus einer Funktion, die Versprechen zurückgibt, zwingt Sie dazu, sowohl ein } catch {
als auch ein .catch
zu verwenden. Personen, die promisifizierte APIs verwenden, erwarten nicht, dass Versprechen Ausnahmen auslösen. Wenn Sie sich nicht sicher sind, wie asynchrone APIs in JS funktionieren - sehen Sie sich bitte zuerst diese Antwort an.
1. DOM-Load oder andere einmalige Ereignisse:
Das Erstellen von Versprechen bedeutet im Allgemeinen, anzugeben, wann sie aufgelöst werden - das bedeutet, wann sie in die erfüllte oder abgelehnte Phase übergehen, um anzuzeigen, dass die Daten verfügbar sind (und mit .then
abgerufen werden können).
Mit modernen Versprechen-Implementierungen, die den Promise
-Konstruktor unterstützen, wie nativen ES6-Versprechen:
function load() {
return new Promise(function(resolve, reject) {
window.onload = resolve;
});
}
Sie würden das resultierende Versprechen dann wie folgt verwenden:
load().then(function() {
// Dinge nach dem Laden ausführen
});
Mit Bibliotheken, die aufgeschobene (defered) unterstützen (Lassen Sie uns hier $q für dieses Beispiel verwenden, aber später verwenden wir auch jQuery):
function load() {
var d = $q.defer();
window.onload = function() { d.resolve(); };
return d.promise;
}
Oder mit einer jQuery-ähnlichen API, die einmalig auftretende Ereignisse erfasst:
function done() {
var d = $.Deferred();
$("#myObject").once("click",function() {
d.resolve();
});
return d.promise();
}
2. Einfacher Rückruf (Callback):
Diese APIs sind ziemlich verbreitet, da Rückrufe in JS häufig vorkommen. Schauen wir uns den häufigen Fall an, dass onSuccess
und onFail
vorhanden sind:
function getUserData(userId, onLoad, onFail) { …
Mit modernen Versprechen-Implementierungen, die den Promise
-Konstruktor unterstützen, wie nativen ES6-Versprechen:
function getUserDataAsync(userId) {
return new Promise(function(resolve, reject) {
getUserData(userId, resolve, reject);
});
}
Mit Bibliotheken, die aufgeschobene unterstützen (Lassen Sie uns hier für dieses Beispiel jQuery verwenden, aber oben haben wir auch $q verwendet):
function getUserDataAsync(userId) {
var d = $.Deferred();
getUserData(userId, function(res){ d.resolve(res); }, function(err){ d.reject(err); });
return d.promise();
}
JQuery bietet auch eine $.Deferred(fn)
-Form, die den Vorteil bietet, dass wir einen Ausdruck schreiben können, der der new Promise(fn)
-Form sehr nahe kommt, wie folgt:
function getUserDataAsync(userId) {
return $.Deferred(function(dfrd) {
getUserData(userId, dfrd.resolve, dfrd.reject);
}).promise();
}
Hinweis: Hier nutzen wir die Tatsache aus, dass die resolve
und reject
Methoden eines jQuery-Deffered "abtrennbar" sind; d.h. sie sind an die Instanz eines jQuery.Deferred() gebunden. Nicht alle Bibliotheken bieten dieses Feature.
3. Node-Stil-Rückruf ("Nodeback"):
Node-Stil-Rückrufe (Nodebacks) haben ein spezielles Format, bei dem der Rückruf immer das letzte Argument ist und sein erster Parameter ein Fehler ist. Lassen Sie uns zuerst manuell eines davon zu einem Versprechen machen:
getStuff("dataParam", function(err, data) { …
Zu:
function getStuffAsync(param) {
return new Promise(function(resolve, reject) {
getStuff(param, function(err, data) {
if (err !== null) reject(err);
else resolve(data);
});
});
}
Mit aufgeschobenen (deferreds) können Sie folgendes tun (Lassen Sie uns hier Q für dieses Beispiel verwenden, obwohl Q jetzt die neue Syntax unterstützt die Sie bevorzugen sollten):
function getStuffAsync(param) {
var d = Q.defer();
getStuff(param, function(err, data) {
if (err !== null) d.reject(err);
else d.resolve(data);
});
return d.promise;
}
Im Allgemeinen sollten Sie Dinge nicht zu sehr manuell verabeiten, die meisten Versprechen-Bibliotheken, die auch für Node gedacht waren, sowie native Versprechen in Node 8+ haben eine integrierte Methode zum Umwandeln von Nodebacks in Versprechen. Zum Beispiel
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native Versprechen, nur Node
4. Eine ganze Bibliothek mit Node-Stil-Rückrufen:
Es gibt keine golden Regel hier, Sie promisifizieren sie einzeln. Einige Versprechen-Implementierungen erlauben es jedoch, dies in einem Stapel durchzuführen, zum Beispiel in Bluebird, das Konvertieren einer Nodeback-API in eine Promise-API ist so einfach wie:
Promise.promisifyAll(API);
Oder mit nativen Versprechen in Node:
const { promisify } = require('util');
const promiseAPI = Object.entries(API).map(([key, v]) => ({key, fn: promisify(v)}))
.reduce((o, p) => Object.assign(o, {[p.key]: p.fn}), {});
Notizen:
- Natürlich benötigen Sie in einem
.then
-Handler keine Promisifizierung. Das Zurückgeben eines Versprechens aus einem .then
-Handler wird mit dem Wert dieses Versprechens erfüllt oder abgelehnt. Das Werfen aus einem .then
-Handler ist ebenfalls eine gute Praxis und wird das Versprechen ablehnen - dies ist die berühmte Sicherheit bei der Promise-Werfung.
- In einem tatsächlichen
onload
-Fall sollten Sie addEventListener
anstelle von onX
verwenden.