Warum kann ich nicht einfach einen Error
innerhalb des catch-Rückrufs werfen und den Prozess den Fehler so behandeln lassen, als ob er in einem anderen Bereich wäre?
Wenn ich es nicht tue, wird console.log(err)
nichts ausgedruckt und ich weiß nichts darüber, was passiert ist. Der Prozess endet gerade ...
Beispiel:
function do1() {
return new Promise(function(resolve, reject) {
throw new Error('do1');
setTimeout(resolve, 1000)
});
}
function do2() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
reject(new Error('do2'));
}, 1000)
});
}
do1().then(do2).catch(function(err) {
//console.log(err.stack); // This is the only way to see the stack
throw err; // This does nothing
});
Wenn Rückrufe im Haupt-Thread ausgeführt werden, warum wird der Error
von einem Schwarzen Loch verschluckt?
javascript
asynchronous
promise
throw
es6-promise
demian85
quelle
quelle
.catch(…)
..catch((e) => { throw new Error() })
schreiben.catch((e) => { return Promise.reject(new Error()) })
oder einfach.catch((e) => Promise.reject(new Error()))
Antworten:
Wie andere erklärt haben, liegt das "Schwarze Loch" daran, dass das Werfen in eine
.catch
Kette mit einem abgelehnten Versprechen fortgesetzt wird und Sie keine Fänge mehr haben, was zu einer nicht abgeschlossenen Kette führt, die Fehler verschluckt (schlecht!)Fügen Sie noch einen Haken hinzu, um zu sehen, was passiert:
Ein Fang in der Mitte einer Kette ist nützlich, wenn Sie möchten, dass die Kette trotz eines fehlgeschlagenen Schritts fortgesetzt wird. Ein erneuter Wurf ist jedoch hilfreich, um weiterhin fehlzuschlagen, nachdem Sie beispielsweise Informationen protokolliert oder Bereinigungsschritte ausgeführt haben und möglicherweise sogar den Fehler geändert haben ist geworfen.
Trick
Um den Fehler wie ursprünglich beabsichtigt als Fehler in der Webkonsole anzuzeigen, verwende ich diesen Trick:
Sogar die Zeilennummern sind erhalten, sodass der Link in der Webkonsole mich direkt zu der Datei und Zeile führt, in der der (ursprüngliche) Fehler aufgetreten ist.
Warum es funktioniert
Jede Ausnahme in einer Funktion, die als Handler für die Erfüllung oder Ablehnung von Versprechen bezeichnet wird, wird automatisch in eine Ablehnung des Versprechens umgewandelt, das Sie zurückgeben sollen. Der Versprechen-Code, der Ihre Funktion aufruft, sorgt dafür.
Eine von setTimeout aufgerufene Funktion wird dagegen immer im stabilen JavaScript-Zustand ausgeführt, dh sie wird in einem neuen Zyklus in der JavaScript-Ereignisschleife ausgeführt. Ausnahmen dort werden von nichts erfasst und erreichen die Webkonsole. Da
err
alle Informationen über den Fehler enthalten sind, einschließlich des ursprünglichen Stapels, der Datei und der Zeilennummer, wird dieser weiterhin korrekt gemeldet.quelle
function logErrors(e){console.error(e)}
dann benutze es gernedo1().then(do2).catch(logErrors)
. Antwort selbst ist übrigens großartig, +1window.onerror
Ereignishandler an meinen Server zu senden . Nur mit diesemsetTimeout
Trick kann dies erreicht werden. Andernfallswindow.onerror
wird nie etwas über die Fehler in Promise gehört.console.log
oderpostErrorToServer
, können Sie einfach das tun, was getan werden muss. Es gibt keinen Grund, warum der Codewindow.onerror
nicht in eine separate Funktion zerlegt und von zwei Stellen aus aufgerufen werden kann. Es ist wahrscheinlich noch kürzer als diesetTimeout
Linie.Wichtige Dinge, die Sie hier verstehen sollten
Sowohl die
then
als auch diecatch
Funktionen geben neue Versprechungsobjekte zurück.Durch Werfen oder explizites Ablehnen wird das aktuelle Versprechen in den abgelehnten Zustand versetzt.
Da
then
undcatch
neue Versprechen Objekte zurückgeben, können sie verkettet werden.Wenn Sie einen Versprechen-Handler (
then
odercatch
) werfen oder ablehnen , wird dies im nächsten Ablehnungs-Handler auf dem Verkettungspfad behandelt.Wie von jfriend00 erwähnt, werden die
then
und -Handlercatch
nicht synchron ausgeführt. Wenn ein Handler wirft, endet er sofort. Der Stapel wird also abgewickelt und die Ausnahme geht verloren. Aus diesem Grund lehnt das Auslösen einer Ausnahme das aktuelle Versprechen ab.In Ihrem Fall lehnen Sie innen ab,
do1
indem Sie einError
Objekt werfen . Jetzt befindet sich das aktuelle Versprechen im abgelehnten Zustand und die Kontrolle wird an den nächsten Handler übertragen, wasthen
in unserem Fall der Fall ist.Da der
then
Handler keinen Ablehnungshandler hat,do2
wird der überhaupt nicht ausgeführt. Sie können dies bestätigen, indem Sieconsole.log
es verwenden. Da das aktuelle Versprechen keinen Ablehnungshandler hat, wird es auch mit dem Ablehnungswert aus dem vorherigen Versprechen abgelehnt und die Kontrolle wird an den nächsten Handler übertragencatch
.Wie
catch
bei einem Ablehnungshandlerconsole.log(err.stack);
können Sie den Fehlerstapel-Trace sehen , wenn Sie sich darin befinden. Jetzt werfen Sie einError
Objekt daraus, sodass das von zurückgegebene Versprechencatch
ebenfalls abgelehnt wird.Da Sie dem keinen Ablehnungshandler zugeordnet haben
catch
, können Sie die Ablehnung nicht beobachten.Sie können die Kette teilen und dies besser verstehen
Die Ausgabe, die Sie erhalten, ist ungefähr so
Innerhalb des
catch
Handlers 1 erhalten Sie den Wert despromise
Objekts als abgelehnt.Ebenso wird das vom
catch
Handler 1 zurückgegebene Versprechen mit demselben Fehler abgelehnt, mit dem daspromise
abgelehnt wurde, und wir beobachten es im zweitencatch
Handler.quelle
.then()
Handler asynchron sind (der Stapel wird abgewickelt, bevor sie ausgeführt werden), sodass Ausnahmen in ihnen in Ablehnungen umgewandelt werden müssen, da es sonst keine Ausnahmebehandler gibt, die sie abfangen können.Ich habe die oben beschriebene
setTimeout()
Methode ausprobiert ...Ärgerlicherweise fand ich das völlig unprüfbar. Da es einen asynchronen Fehler auslöst, können Sie ihn nicht in eine
try/catch
Anweisung einschließen, da dercatch
Wille zum Zeitpunkt des Auslösens des Fehlers nicht mehr zuhört.Ich habe wieder nur einen Listener verwendet, der perfekt funktioniert hat und der, da JavaScript so verwendet werden soll, sehr gut testbar war.
quelle
Gemäß der Spezifikation (siehe 3.III.d) :
Das heißt, wenn Sie eine Ausnahme in die
then
Funktion werfen , wird diese abgefangen und Ihr Versprechen wird abgelehnt.catch
macht hier keinen Sinn, es ist nur eine Abkürzung zu.then(null, function() {})
Ich denke, Sie möchten unbehandelte Ablehnungen in Ihrem Code protokollieren. Die meisten Versprechungsbibliotheken feuern ein
unhandledRejection
dafür. Hier ist ein relevanter Kern mit einer Diskussion darüber.quelle
unhandledRejection
Hook für serverseitiges JavaScript ist. Auf der Clientseite haben verschiedene Browser unterschiedliche Lösungen. Wir haben es noch nicht standardisiert, aber es kommt langsam aber sicher an.Ich weiß, dass dies etwas spät ist, aber ich bin auf diesen Thread gestoßen, und keine der Lösungen war für mich einfach zu implementieren, daher habe ich mir eine eigene ausgedacht:
Ich habe eine kleine Hilfsfunktion hinzugefügt, die ein Versprechen zurückgibt, wie folgt:
Wenn ich dann eine bestimmte Stelle in einer meiner Versprechensketten habe, an der ich einen Fehler auslösen (und das Versprechen ablehnen) möchte, kehre ich einfach mit meinem konstruierten Fehler von der obigen Funktion zurück:
Auf diese Weise habe ich die Kontrolle darüber, zusätzliche Fehler aus der Versprechenskette herauszuwerfen. Wenn Sie auch "normale" Versprechungsfehler behandeln möchten, erweitern Sie Ihren Fang, um die "selbst geworfenen" Fehler separat zu behandeln.
Hoffe das hilft, es ist meine erste Stackoverflow Antwort!
quelle
Promise.reject(error)
stattnew Promise(function (resolve, reject){ reject(error) })
(was sowieso eine return-Anweisung benötigen würde)Ja verspricht Schluckfehler, und Sie können sie nur abfangen
.catch
, wie in anderen Antworten ausführlicher erläutert. Wenn Sie sich in Node.js befinden und das normalethrow
Verhalten reproduzieren möchten, können Sie den Stack-Trace auf die Konsole drucken und den Prozess beendenquelle
unhandledRejection
Veranstaltung teilnehmen