Aktualisieren:
Um zukünftigen Zuschauern dieses Beitrags zu helfen, habe ich diese Demo von Plumas Antwort erstellt .
Frage:
Mein Ziel scheint ziemlich einfach zu sein.
step(1)
.then(function() {
return step(2);
}, function() {
stepError(1);
return $q.reject();
})
.then(function() {
}, function() {
stepError(2);
});
function step(n) {
var deferred = $q.defer();
//fail on step 1
(n === 1) ? deferred.reject() : deferred.resolve();
return deferred.promise;
}
function stepError(n) {
console.log(n);
}
Das Problem hierbei ist, dass, wenn ich in Schritt 1 versage, beide stepError(1)
UND stepError(2)
ausgelöst werden. Wenn ich es nicht tun , return $q.reject
dann stepError(2)
wird nicht gefeuert werden, aber step(2)
wird, was ich verstehe. Ich habe alles erreicht, außer was ich versuche zu tun.
Wie schreibe ich Versprechen, damit ich bei Ablehnung eine Funktion aufrufen kann, ohne alle Funktionen in der Fehlerkette aufzurufen? Oder gibt es einen anderen Weg, dies zu erreichen?
Hier ist eine Live-Demo , mit der Sie arbeiten können.
Aktualisieren:
Ich habe es irgendwie gelöst. Hier fange ich den Fehler am Ende der Kette ab und übergebe die Daten an, reject(data)
damit ich weiß, welches Problem in der Fehlerfunktion zu behandeln ist. Dies entspricht eigentlich nicht meinen Anforderungen, da ich nicht von den Daten abhängig sein möchte. Es wäre lahm, aber in meinem Fall wäre es sauberer, einen Fehlerrückruf an die Funktion zu übergeben, anstatt sich auf die zurückgegebenen Daten zu verlassen, um zu bestimmen, was zu tun ist.
step(1)
.then(function() {
return step(2);
})
.then(function() {
return step(3);
})
.then(false,
function(x) {
stepError(x);
}
);
function step(n) {
console.log('Step '+n);
var deferred = $q.defer();
(n === 1) ? deferred.reject(n) : deferred.resolve(n);
return deferred.promise;
}
function stepError(n) {
console.log('Error '+n);
}
Promise.prototype.catch()
Beispiele auf MDN zeigen eine Lösung für genau dieselben Probleme.Antworten:
Der Grund, warum Ihr Code nicht wie erwartet funktioniert, ist, dass er tatsächlich etwas anderes tut, als Sie denken.
Angenommen, Sie haben etwa Folgendes:
Um besser zu verstehen, was passiert, tun wir so, als wäre dies synchroner Code mit
try
/catch
blockiert:Der
onRejected
Handler (das zweite Argument vonthen
) ist im Wesentlichen ein Fehlerkorrekturmechanismus (wie eincatch
Block). Wenn ein Fehler eingegeben wirdhandleErrorOne
, wird er vom nächsten catch-Block (catch(e2)
) abgefangen und so weiter.Dies ist offensichtlich nicht das, was Sie beabsichtigt haben.
Nehmen wir an, wir möchten, dass die gesamte Auflösungskette versagt, egal was schief geht:
Hinweis: Wir können das
handleErrorOne
dort belassen, wo es ist, da es nur aufgerufen wird, wennstepOne
es abgelehnt wird (es ist die erste Funktion in der Kette, sodass wir wissen, dass wenn die Kette zu diesem Zeitpunkt abgelehnt wird, dies nur aufgrund des Versprechens dieser Funktion möglich ist). .Die wichtige Änderung besteht darin, dass die Fehlerbehandlungsroutinen für die anderen Funktionen nicht Teil der Hauptversprechen-Kette sind. Stattdessen hat jeder Schritt eine eigene "Unterkette" mit einer
onRejected
, die nur aufgerufen wird, wenn der Schritt abgelehnt wurde (aber von der Hauptkette nicht direkt erreicht werden kann).Der Grund dafür ist, dass beide
onFulfilled
undonRejected
optionale Argumente für diethen
Methode sind. Wenn ein Versprechen erfüllt (dh gelöst) wird und der nächstethen
in der Kette keinenonFulfilled
Handler hat, wird die Kette fortgesetzt, bis es einen mit einem solchen Handler gibt.Dies bedeutet, dass die folgenden zwei Zeilen äquivalent sind:
Die folgende Zeile entspricht jedoch nicht den beiden oben genannten:
$q
Angulars Versprechensbibliothek basiert auf derQ
Bibliothek von kriskowal (die eine reichhaltigere API hat, aber alles enthält, was Sie finden können$q
). Die API-Dokumente von Q auf GitHub könnten sich als nützlich erweisen. Q implementiert die Promises / A + -Spezifikation , in der detailliert beschrieben wird, wiethen
und wie das Verhalten bei der Auflösung von Versprechen genau funktioniert.BEARBEITEN:
Denken Sie auch daran, dass Sie, wenn Sie in Ihrem Fehlerbehandler aus der Kette ausbrechen möchten, ein abgelehntes Versprechen zurückgeben oder einen Fehler auslösen müssen (der automatisch abgefangen und in ein abgelehntes Versprechen eingewickelt wird). Wenn Sie kein Versprechen zurückgeben, wird
then
der Rückgabewert in ein Lösungsversprechen für Sie eingeschlossen.Dies bedeutet, dass Sie, wenn Sie nichts zurückgeben, effektiv ein gelöstes Versprechen für den Wert zurückgeben
undefined
.quelle
if you don't return anything, you are effectively returning a resolved promise for the value undefined.
Danke @plumastepOne().then(stepTwo, handleErrorOne)
`stepOne (). then (null, handleErrorOne) .then (stepTwo)` Sind diese wirklich äquivalent? Ich denke, im Falle einer Ablehnung wird instepOne
der zweiten Codezeile ausgeführt,stepTwo
aber die erste wird nur ausgeführthandleErrorOne
und gestoppt. Oder fehlt mir etwas?Etwas spät zur Party, aber diese einfache Lösung hat bei mir funktioniert:
Dadurch können Sie aus der Kette ausbrechen .
quelle
.then(user => { if (user) return Promise.reject('The email address already exists.') })
.then(user => { if (user) throw 'The email address already exists.' })
Was Sie brauchen, ist eine sich wiederholende
.then()
Kette mit einem Sonderfall zum Starten und einem Sonderfall zum Beenden.Der Kniff besteht darin, die Schrittnummer des Fehlerfalls an einen endgültigen Fehlerbehandler weiterzuleiten.
step(1)
bedingungslos anrufen ..then()
mit folgenden Rückrufen:.then()
Handler ohne Erfolg und einen letzten Fehlerhandler.Sie können das Ganze in Langschrift schreiben, aber es ist einfacher, das Muster mit benannten, verallgemeinerten Funktionen zu demonstrieren:
siehe Demo
Beachten Sie, wie in
step()
, der Aufgeschobene abgelehnt oder aufgelöst wirdn
, wodurch dieser Wert den Rückrufen im nächsten Teil.then()
der Kette zur Verfügung steht. EinmalstepError
aufgerufen, wird der Fehler wiederholt erneut ausgelöst, bis er von behandelt wirdfinalError
.quelle
Wenn Sie ablehnen, sollten Sie einen Ablehnungsfehler übergeben und dann die Schrittfehlerbehandlungsroutinen in eine Funktion einschließen, die prüft, ob die Zurückweisung bis zum Ende der Kette verarbeitet oder "erneut geworfen" werden soll:
Was Sie auf der Konsole sehen würden:
Hier ist ein Arbeitscode https://jsfiddle.net/8hzg5s7m/3/
Wenn Sie für jeden Schritt eine bestimmte Behandlung haben, könnte Ihr Wrapper wie folgt aussehen:
dann deine Kette
quelle
Wenn ich das richtig verstehe, soll nur der Fehler für den fehlgeschlagenen Schritt angezeigt werden, oder?
Das sollte so einfach sein, wie den Fehlerfall des ersten Versprechens dahingehend zu ändern:
Wenn
$q.reject()
Sie im Fehlerfall des ersten Schritts zurückkehren , lehnen Sie dieses Versprechen ab, wodurch der errorCallback im zweiten aufgerufen wirdthen(...)
.quelle
step(2)
. Jetzt habe ich es einfach noch einmal versucht, es passiert nicht. Ich bin so verwirrt.return step(2);
sollte immer nur aufgerufen werden, wenn siestep(1)
erfolgreich aufgelöst wurde.return $q.reject()
, wird die Kette weiterlaufen. In diesem Fallreturn response
vermasselt. Siehe dies: jsbin.com/EpaZIsIp/6/edithttp://jsbin.com/EpaZIsIp/20/edit
Oder für eine beliebige Anzahl von Schritten automatisiert:
http://jsbin.com/EpaZIsIp/21/edit
quelle
deferred.reject(n)
ich anrufe,Versuchen Sie, dies wie libs zu verwenden:
https://www.npmjs.com/package/promise-chain-break
quelle
Wenn Sie dieses Problem mit async / await lösen möchten:
quelle
Fügen Sie Fehlerbehandlungsroutinen als separate Kettenelemente direkt zur Ausführung der Schritte hinzu:
oder mit
catch()
:Hinweis: Dies ist im Grunde das gleiche Muster, das Pluma in seiner Antwort vorschlägt, jedoch unter Verwendung der Benennung des OP.
quelle
Gefundene
Promise.prototype.catch()
Beispiele auf MDN unten sehr hilfreich.(Die akzeptierte Antwort erwähnt,
then(null, onErrorHandler)
was im Grunde das gleiche ist wiecatch(onErrorHandler)
.)quelle
Die beste Lösung besteht darin, Ihre Versprechenskette umzugestalten, um ES6 zu verwenden. Dann können Sie einfach von der Funktion zurückkehren, um den Rest des Verhaltens zu überspringen.
Ich habe über ein Jahr lang meinen Kopf gegen dieses Muster geschlagen und mit Warten ist es der Himmel.
quelle
Verwenden Sie ein SequentialPromise-Modul
Absicht
Stellen Sie ein Modul bereit, dessen Aufgabe es ist, Anforderungen nacheinander auszuführen, während der aktuelle Index jeder Operation auf ordinale Weise verfolgt wird. Definieren Sie die Operation aus Gründen der Flexibilität in einem Befehlsmuster .
Teilnehmer
execute
Methode zum Verketten und Verfolgen jeder Operation. SequentialPromise gibt eine Versprechen-Kette von allen ausgeführten Operationen zurück.execute
Methode auf, während eine ordinale Liste von Optionen für jede Operation übergeben wird.Folgen
Verwenden Sie SequentialPromise, wenn das ordinale Verhalten der Promise-Auflösung erforderlich ist. SequentialPromise verfolgt den Index, für den ein Versprechen abgelehnt wurde.
Implementierung
Kern
SequentialPromise
quelle
Wenn Sie irgendwann zurückkehren, werden
Promise.reject('something')
Sie in den Fangblock zum Versprechen geworfen.Wenn das erste Versprechen kein Ergebnis zurückgibt, wird in der Konsole nur "Kein Ergebnis" angezeigt.
quelle