Wie man in Promise ungefangene Ausnahmen fängt

75

Gibt es eine Möglichkeit, alle Ausnahmen einschließlich Promise-Ausnahmen global abzufangen? Beispiel:

    window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
        alert("Error occured: " + errorMsg);//or any message
        return false;
    }

    var myClass = function(){

    }


    var pr = new Promise(function(resolve, react){
        var myInstance = new myClass();
        myInstance.undefinedFunction(); // this will throw Exception
        resolve(myInstance);
    });


    pr.then(function(result){
        console.log(result);
    });

    // i know right will be this:
    // pr.then(function(result){
    //     console.log(result);
    // }).catch(function(e){
    //     console.log(e);
    // });

Dieses Skript stirbt stillschweigend ohne Fehler. Nichts in Feuerwanze.

Meine Frage ist, ob ich einen Fehler mache und vergessen habe, ihn zu fangen. Gibt es eine Möglichkeit, ihn global zu fangen?

John
quelle

Antworten:

30

Update, native Versprechen machen jetzt in den meisten Browsern Folgendes:

window.addEventListener("unhandledrejection", function(promiseRejectionEvent) { 
    // handle error here, for example log   
});

Wir haben das neulich gerade besprochen.

So würden Sie das mit Bluebird machen:

window.onpossiblyunhandledexception = function(){
    window.onerror.apply(this, arguments); // call
}

window.onerror = function(err){
    console.log(err); // logs all errors
}

Mit Bluebird ist es auch möglich zu verwenden Promise.onPossiblyUnhandledRejection. Die Aufrufe für donewerden nicht benötigt, da die Bibliothek im Gegensatz zu Q selbst nicht behandelte Ablehnungen erkennt (UPDATE 2016 - Ich habe jetzt Code für Q geschrieben und dies geschieht).

Native Versprechen - sie werden irgendwann entweder an window.onerror oder an einen neuen Handler gemeldet, aber der Spezifikationsprozess ist noch nicht abgeschlossen - können Sie hier verfolgen .

Benjamin Gruenbaum
quelle
> in den meisten Browsern: Laut MDN ist Chrome der einzige Browser, der dies ab Juni 2018 unterstützt.
John Hatton
1
@ JohnHatton Laut Statcounter sind die meisten Browser Chrome. Das heißt - Sie können immer eine Versprechensbibliothek verwenden, um dieses Verhalten zu erhalten. Ich habe Leute in Firefox (das es unter einer Flagge hat) Safari und Edge angerufen, um zu sehen, ob wir dies bald in mehr Browsern tun können.
Benjamin Gruenbaum
1
"Die meisten Browser sind Chrome" OK, ich nehme an, "der in den meisten Sitzungen verwendete Browser" ist eine Definition von "die meisten Browser" ... nicht die, die ich auswählen würde.
John Hatton
15

Die meisten Versprechen-Implementierungen bieten derzeit nicht die Art von Funktionalität, auf die Sie sich beziehen, aber eine Reihe von Versprechen-Bibliotheken von Drittanbietern (einschließlich Q und Bluebird ) bieten eine done()Methode, mit der nicht erfasste Fehler abgefangen und erneut ausgegeben und somit an die Konsole ausgegeben werden .

( Bearbeiten: siehe Benjamin Gruenbaums Antwort zu Bluebirds Funktionen für den Umgang mit nicht erfassten Ausnahmen)

Wenn Sie das haben, müssen Sie nur die Faustregel befolgen, dass jedes Versprechen, das Sie verwenden, entweder zurückgegeben oder beendet werden sollte mit .done():

pr.then(function(result){
    console.log(result);
})
.done();

Um aus der Q API-Referenz zu zitieren:

Die goldene Regel done gegen den then Gebrauch lautet: Entweder return Ihr Versprechen an jemand anderen, oder wenn die Kette mit Ihnen endet, rufen Sie an, um sie done zu beenden. Das Beenden mit catch reicht nicht aus, da der catchHandler selbst einen Fehler auslösen kann.

Mir ist klar, dass dies immer noch ein wenig zusätzliche Arbeit erfordert und Sie dies immer noch vergessen können, aber es ist eine Verbesserung gegenüber der Notwendigkeit, .catch()jeden Fehler explizit zu behandeln, insbesondere aus dem oben genannten Grund.

JLRishe
quelle
2
Tatsächlich erledigt ist nicht anders als einfach zu tun .catch(thrower)(oder dasselbe mit einer Verknüpfung zu tun, z Promise.prototype.done = .... B. Hinzufügen . Daher ist es für diesen Zweck ziemlich sinnlos, insbesondere in Bluebird, wo normalerweise nicht
erfasste
1
@Esailija Kannst du das näher erläutern? Was wäre throwerin diesem Fall?
JLRishe
1
@Esailija Ok, es kann in Bluebird nicht benötigt werden, aber es ist immer noch der einfachste Weg, um zu verhindern, dass Fehler in einigen anderen Implementierungen (AFAIK) vollständig verschluckt werden, also würde ich es nicht als sinnlos bezeichnen.
JLRishe
1
@JLRishe Ich denke, sein Punkt ist, dass Leute keine Versprechen-Implementierungen verwenden sollten, die Fehler verschlucken (wie jQuery, Q, $ q usw.) und Versprechen-Bibliotheken verwenden sollten, mit denen Sie unbehandelte Ablehnungen wie Bluebird, When, RSVP usw. verfolgen können
Benjamin Gruenbaum
1
Ich meinte sinnlos als ein anderes Konzept als den verherrlichten Fangwurf
Esailija
4

In Node.js können Sie Folgendes verwenden:

process.on('unhandledRejection', (reason, promise) => {
  console.error(`Uncaught error in`, promise);
});
JussiR
quelle
Dies ist nützlich beim Debuggen im Unbekannten, aber wenn Sie wissen, welches Versprechen Sie haben und Zugriff haben, sollten Sie es verwenden .catchoder .donefür eine bessere Wartbarkeit
tsuz
In einer neueren Version von Node ist dies tatsächlich zum Standardverhalten geworden. So werden Fehler in nicht erfassten Versprechungen protokolliert, auch ohne diesen Code hinzuzufügen.
JussiR
Wie konnten Sie das Versprechen bekommen, das dies verursacht hat? oder die Funktionsdatei? Die Sache ist dieser Grund. Stapel ist immer undefiniert.
Loebre
2

Wenn Sie Code schreiben, der sowohl in einem Browser als auch in einer Node.js-Umgebung ausgeführt werden kann, können Sie Ihren Code in eine selbstausführende Funktion wie die folgende einschließen:

var isNode = (typeof process !== 'undefined' && typeof process.on !== 'undefined');
(function(on, unhandledRejection) {
    // PUT ANY CODE HERE
    on(unhandledRejection, function(error, promise) {
        console.error(`Uncaught error in`, promise);
    });
    // PUT ANY CODE HERE
})(
    isNode ? process.on.bind(process) : window.addEventListener.bind(window),
    isNode ? "unhandledRejection" : "unhandledrejection"
);

Was würde passieren, wenn Sie diesen Code verwenden:

  • Wenn Sie es in einer Node.js-Umgebung ausführen würden, würde Ihr Handler an das Prozessobjekt angehängt und ausgeführt, wenn Sie eine nicht erfasste Ausnahme in einem Versprechen haben.

  • Wenn Sie es in einer Browserumgebung ausführen würden, würde Ihr Handler an das Fensterobjekt angehängt und ausgeführt, wenn Sie eine nicht erfasste Ausnahme in einem Versprechen haben und Ihr Browser das unhandledrejectionEreignis unterstützt .

  • Wenn Sie es in einer Browserumgebung ohne Unterstützung für ausführen unhandledrejection, können Sie Ihre nicht erfasste Ausnahme nicht abfangen, und Ihr unhandledrejectionEreignishandler wird niemals ausgelöst. Sie erhalten jedoch keine Fehler, wenn keine nicht erfassten Ausnahmen vorliegen.

John Slegers
quelle
1

Wenn Sie native Promise verwenden, ist dies ziemlich einfach.
Sie müssen nur .catcheinige wo ablehnen.

ajax(request).catch(function(rejected){
      console.log(rejected);
});

Wenn ich es nicht irgendwo fange, wird sich das nicht gefangene Versprechen immer wieder zeigen. Aber wenn ich es irgendwo fange ...

redyyu
quelle
7
Dies beantwortet die gestellte Frage nicht.
Csauve