Wie kann man Javascript-Versprechen debuggen?


74

Ich versuche zu verstehen, wie man asynchronen Code debuggt, der auf Versprechungen basiert. Mit Versprechen meine ich ECMAScript 6-basierte Versprechen und mit Debuggen meine ich die Verwendung des eingebauten Chrome- oder Firefox-Debuggers.

Ich habe Probleme damit - wenn ein Fehler auftritt, kann ich den Stack-Trace scheinbar nicht abrufen, egal wie ich ihn "ablehne".

Ich habe diese ausprobiert:

console.log(new Error('Error occured'));
throw new Error('Throwing an Error');
return new Error('Error returned by the onRejected function');
reject(new Error('Pass Error to the reject function'));

Aber keiner von diesen gibt den tatsächlichen Fehler im Code oder in der Stapelverfolgung zurück.

Meine Frage ist also: Wie kann man Javascript-Versprechen richtig debuggen?


Basierend auf html5rocks.com/de/tutorials/es6/promises würde ich es lieber tun: reject('Error')Aber können Sie eine jsfiddle posten, damit wir etwas Konkretes haben, mit dem wir arbeiten können?
Renra

3
Dies ist einer der Hauptvorteile der Arbeit mit der Bluebird-Versprechensbibliothek. Es erfasst Stack-Traces für Sie, wenn Sie eine unbehandelte Ablehnung oder Ausnahme in Ihrem Versprechen-Code haben. Ich habe es mit node.js verwendet und es spart eine TONNE Zeit. Ich weiß ehrlich gesagt nicht, wie ich die gleiche Funktionalität mit eingebauten Versprechungen erreichen kann.
jfriend00

2
@ Renra, bitte gib keine schlechten Ratschläge - das Werfen (oder Ablehnen mit) Primitiven ist eine böse Angewohnheit, die das Debuggen viel schwieriger macht.
Benjamin Gruenbaum

Antworten:


60

Dies ist ein großartiges Thema, über das man diskutieren kann. Die traurige Nachricht ist, dass dies mit einheimischen Versprechungen ziemlich schwierig ist.

Das Debuggen von rohen ES6-Versprechungen in Chrome ist schrecklich. Dies liegt daran, dass sie Fehler stillschweigend unterdrücken. Wenn Sie einen Fang weglassen, erhalten Sie keinen Hinweis darauf, dass das Versprechen fehlgeschlagen ist. Update: Chrome protokolliert jetzt nicht behandelte Ablehnungen (siehe diesen Link für)

 Promise.resolve("foo").then(function(){
      throw new Error("You will never see this");// silent failure
 });

In Firefox sind die Dinge etwas besser, da sie eine unbehandelte Ablehnungserkennung durchführen. Es ist jedoch immer noch ein Flakey, und wenn Sie das Versprechen irgendwo vergeben, funktioniert es nicht.

Was kann man also tun?

Bluebird einschließen - es ist eine Obermenge von ES6-Versprechungen und Sie können es direkt austauschen, es hat eine reichhaltigere API, es ist schneller und es hat erstaunliche Stapelspuren . Es wurde mit Blick auf das Debuggen entwickelt und bietet hervorragende Möglichkeiten zur Fehlerbehandlung.

Sobald Sie Bluebird aufgenommen haben, rufen Sie an:

Promise.longStackTraces();

Das wird es etwas verlangsamen (es wird immer noch sehr schnell sein) und Ihnen erstaunliche Fehlermeldungen geben. Zum Beispiel:

Promise.resolve().then(function outer() {
    return Promise.resolve().then(function inner() {
        return Promise.resolve().then(function evenMoreInner() {
            a.b.c.d()
        });
    });
});

Bei nativen Versprechungen - dies ist ein stiller Fehler und sehr schwer zu debuggen - wird bei Bluebird-Versprechungen standardmäßig ein großer roter Fehler in Ihrer Konsole angezeigt, der Folgendes ergibt:

ReferenceError: a is not defined
    at evenMoreInner (<anonymous>:6:13)
From previous event:
    at inner (<anonymous>:5:24)
From previous event:
    at outer (<anonymous>:4:20)
From previous event:
    at <anonymous>:3:9
    at Object.InjectedScript._evaluateOn (<anonymous>:581:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:540:52)
    at Object.InjectedScript.evaluate (<anonymous>:459:21)

Sobald Sie mit dem Debuggen fertig sind, können Sie es austauschen und zu den nativen Versprechungen zurückkehren. Persönlich schätze ich es zu wissen, dass ich Fehler in der Produktion habe, deshalb empfehle ich es nicht, aber es ist auf jeden Fall machbar.


1
Was es wahrscheinlich verlangsamt, ist die Tatsache, dass es jede Zeilennummer in Ihrem Code beschriften muss, wenn der Stapel erstellt wird, was längeren Code etwas mehr machen würde. Obwohl für das Debuggen, ist dies definitiv die Verlangsamung wert.
Neil

2
Nun, es müssen die Stapelspuren zusammengefügt werden, aber nur bei Fehlern - im Allgemeinen ist Bluebird sehr schnell und die Leistung ist immer noch sehr gut mit langen Stapelspuren auf der Clientseite.
Benjamin Gruenbaum

Vielen Dank für Ihre Antwort, zumindest weiß ich jetzt, dass ich nicht der einzige bin, der damit zu kämpfen hat. Werde auf jeden Fall in Bluebird schauen. Außerdem wird versucht, eine JSFiddle zu erstellen, um zu veranschaulichen, dass keine Fehler angezeigt werden, wenn das Versprechen fehlgeschlagen ist. Und außerdem - ein Versprechen, bei dem der Fehler aufgetreten ist, kann tatsächlich den Status "behoben" anzeigen. Ich denke, dies wird passieren, wenn das Versprechen nicht das letzte in der Kette ist.
YemSalat

1
BLUEBIRD! Genial. Beeindruckend. Ich habe Q benutzt und war super frustriert.
RandallB

1
Ja, Chrome hat tatsächlich ein großes Versprechen, Debug-Panel zu kommen :) In Kanarienvogel wird es dann aktualisiert
Benjamin Gruenbaum

13

Diese Antwort ist eine Ergänzung zu Benjamin Gruenbaums Antwort: Wenn Sie eine catch-Anweisung in der Versprechenskette verwenden, erhalten Sie die Stapelverfolgung durch error.stack :

        Promise.longStackTraces();

        function outer() {
            return Promise.resolve();
        }

        function inner() {
            return Promise.resolve();
        }

        function evenMoreInner() {
            a.b.c.d()
        }

        Promise.resolve()
            .then(outer)
            .then(inner)
            .then(evenMoreInner())
            .catch(function (err) {
                    console.log(err.message);
                    console.log(err.stack);
                });

Fehlermeldung:

ReferenceError: a is not defined
at evenMoreInner (test/test_promise.js:58:17)  <<<< HERE's the error!
at Context.<anonymous> (test/test_promise.js:64:23)

2
Danke, ich habe das stackAnwesen verpasst und mich gefragt, wohin es gegangen ist, haha!
NiCk Newman

4
Nur um diesen Punkt zu wiederholen ... Ich wurde von der Tatsache getäuscht, dass console.log(err)err.stack nicht enthalten ist. Sie müssen console.log(err.stack)die Details sehen.
Chris Dennis

Es hat bei mir nicht funktioniert, bis ich das Kontrollkästchen "asynchron" aktiviert habe, wie in der Antwort von goat erwähnt. stackoverflow.com/a/25827268/247696
Flimm

13

* Dies beantwortet Ihre Frage nicht direkt, kann aber dennoch hilfreich sein.

Chrome devtools hat kürzlich eine neue Funktion erhalten, die zum Debuggen von asynchronem Code nützlich ist, z. B. Promises.

http://www.html5rocks.com/de/tutorials/developertools/async-call-stack/

Aktivieren Sie grundsätzlich das Kontrollkästchen "asynchron" auf der Registerkarte "Quellen", und Chrome rekonstruiert den Aufrufstapel für Sie so, als wäre es synchroner Code.

Bildschirmfoto


1
Es werden immer noch keine Fehler auf die Konsole gedruckt und es werden keine Informationen darüber bereitgestellt, wo der Fehler aufgetreten ist. Mit Fehlern meine ich Dinge, die Sie nicht "abfangen"
möchten

Ein Tippfehler sollte in der Konsole angezeigt werden, auch in einem Versprechen.
Dandavis

4
@ Dandavis nein wird es nicht.
Benjamin Gruenbaum

8
Google Chrome unterstützt jetzt standardmäßig Async-Anrufstapel und hat das Kontrollkästchen entfernt: developer.google.com/web/updates/2017/05/…
Dan Salo

1

Sie scheinen mit Debug-Tools in Chrome zu arbeiten. Weitere Informationen finden Sie in diesem Thread.

https://code.google.com/p/v8/issues/detail?id=3093

Ich habe nicht überprüft, ob dies bereits in der Entwickler- oder Beta-Version ist, aber ich hoffe, dass es bald sein wird. Es könnte dann im Januar 2015 oder so in der normalen Version enthalten sein (nur eine persönliche Vermutung, absolut kein Versprechen, da ich nicht einmal für Google arbeite).


Ich sehe derzeit keine spezifischen Debug-Tools für Versprechen in Chrome. Machst du?
Flimm

1
Google hat die Funktion vor einigen Monaten entfernt. Versuchen Sie stattdessen den asynchronen Helfer. siehe dieses Commit Über asynchrones Debuggen
Johan Bergens

1

Der beste Weg, um versprochene Fehler zu beheben, besteht darin, sich das unhandledRejectionEreignis von Ihnen anzuhören process.

Hier erfahren Sie beispielsweise, wie Sie es einrichten und einen Stack-Trace sichern können ...

 process.on('unhandledRejection', (reason, p) => {
   console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
   // Stack Trace
   console.log(reason.stack);
 });

Wo würden Sie diesen Code ablegen? Ich habe einen Fehler in einem Versprechen und kann nicht einmal finden, in welcher Datei ich suchen soll ...
Stephane

0

Sie können eine console.log () -Anweisung in die .then () -Funktion des Promise-Objekts einfügen: Sie können bei Bedarf auch die .catch () - Anweisung hinzufügen.

genericDbCaller(dbName, collectionName, dbFunctionName, params) {
        return new Promise((resolve, reject) => {
            DatabaseContext.instance.getDbConn(dbName)
                .then((dbConn) => {
                    dbConn.get(collectionName)[dbFunctionName].apply(null, params)
                        .then(

                            (docs) =>{
                    ----->>>        console.log(docs);  
                            resolve(docs);
                        })
                        .catch((e)=>{
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.