Fehlerbehebung bei asynchronem JavaScript mit Chrome-Entwicklertools

Pearl Chen

Einführung

Eine leistungsstarke Funktion, die JavaScript so besonders macht, ist die Möglichkeit, asynchron über Callback-Funktionen zu arbeiten. Wenn Sie asynchrone Callbacks zuweisen, können Sie ereignisgesteuerten Code schreiben. Es macht jedoch auch das Aufspüren von Fehlern erschwinglich, da das JavaScript nicht linear ausgeführt wird.

Glücklicherweise finden Sie jetzt in den Chrome-Entwicklertools den vollständigen Aufrufstapel asynchroner JavaScript-Callbacks.

Ein kurzer Teaser-Überblick über asynchrone Aufrufstacks.
Eine kurze Übersicht über asynchrone Aufrufstacks. (Wir werden den Ablauf dieser Demo in Kürze erläutern.)

Sobald Sie die asynchrone Aufrufstack-Funktion in den Entwicklertools aktiviert haben, können Sie den Status Ihrer Webanwendung zu verschiedenen Zeitpunkten aufrufen. Durchlaufen Sie den vollständigen Stacktrace für einige Event-Listener, setInterval, setTimeout, XMLHttpRequest, Promise, requestAnimationFrame, MutationObservers und mehr.

Während Sie den Stacktrace durchlaufen, können Sie auch den Wert einer Variablen zu diesem bestimmten Zeitpunkt der Laufzeitausführung analysieren. Wie eine Zeitmaschine für Ihre Ausdrücke.

Wir aktivieren diese Funktion und sehen uns einige dieser Szenarien an.

Asynchrones Debugging in Chrome aktivieren

Probiere diese neue Funktion aus, indem du sie in Chrome aktivierst. Gehen Sie in den Chrome Canary-Entwicklertools zum Bereich Quellen.

Rechts neben dem Bereich Aufrufstapel befindet sich das neue Kästchen "Asynchron". Klicken Sie das Kästchen an, um das asynchrone Debugging zu aktivieren oder zu deaktivieren.

Aktivieren oder deaktivieren Sie die asynchrone Funktion.

Verzögerte Timer-Ereignisse und XHR-Antworten erfassen

Bestimmt haben Sie Folgendes in Gmail schon einmal gesehen:

Gmail versucht, eine E-Mail zu senden.

Wenn es ein Problem beim Senden der Anfrage gibt (entweder treten auf dem Server Probleme auf oder es gibt Probleme mit der Netzwerkverbindung auf Clientseite), versucht Gmail automatisch, die Nachricht nach einer kurzen Zeitüberschreitung noch einmal zu senden.

Um zu sehen, wie asynchrone Aufrufstacks uns helfen können, verzögerte Timerereignisse und XHR-Antworten zu analysieren, habe ich diesen Ablauf mit einem Beispiel für Gmail neu erstellt. Den vollständigen JavaScript-Code findest du unter dem obigen Link. Der Ablauf sieht jedoch so aus:

Flussdiagramm für ein Beispiel für ein Gmail-Beispiel.
Im Diagramm oben sind die blau hervorgehobenen Methoden dafür besonders nützlich, da diese Methoden asynchron funktionieren.

Da wir uns in früheren Versionen der Entwicklertools nur den Aufrufstapelbereich angesehen haben, würde ein Haltepunkt in postOnFail() wenig Informationen darüber liefern, von wo postOnFail() aufgerufen wurde. Beachten Sie jedoch den Unterschied beim Aktivieren asynchroner Stacks:

Vorher
Ein Haltepunkt wurde in einem Gmail-Beispiel ohne asynchrone Aufrufstacks festgelegt.
Der Aufrufstack-Bereich ohne asynchrones Aktivieren.

Hier sehen Sie, dass postOnFail() von einem AJAX-Callback initiiert wurde. Es gibt jedoch keine weiteren Informationen.

Nachher
Haltepunkt in Gmail-Beispiel mit asynchronen Aufrufstacks festgelegt
Der Aufrufstack-Bereich mit asynchronem Aktivieren.

Hier können Sie sehen, dass die XHR von submitHandler() initiiert wurde. Sehr gut!

Wenn asynchrone Aufrufstacks aktiviert sind, können Sie den gesamten Aufrufstack anzeigen. So können Sie leicht feststellen, ob die Anfrage von submitHandler() (nach dem Klicken auf die Schaltfläche „Senden“) oder von retrySubmit() (mit einer Verzögerung von setTimeout()) initiiert wurde:

submitHandler()
Beispiel für einen Haltepunkt in Gmail-Beispiel mit asynchronen Aufrufstacks
retrySubmit()
Ein weiterer Haltepunkt in Gmail-Beispiel mit asynchronen Aufrufstacks

Ausdrücke asynchron beobachten

Wenn Sie den gesamten Aufrufstack durchlaufen, werden auch die beobachteten Ausdrücke aktualisiert, um den Status wiederzugeben, in dem sie sich zu diesem Zeitpunkt befand.

Beispiel für die Verwendung von Watch-Ausdrücken mit Aysnc-Aufrufstacks

Code aus früheren Bereichen auswerten

Sie können Ausdrücke nicht nur überwachen, sondern direkt im Steuerfeld der JavaScript-Konsole der Entwicklertools mit dem Code aus vorherigen Bereichen interagieren.

Stell dir vor, du bist Dr. Wer und du brauchst ein wenig Hilfe, um die Uhr aus der Zeit vor deiner Ankunft in der Tardis mit der „jetzt“ zu vergleichen. Über die Entwicklertools-Konsole können Sie Werte aus verschiedenen Ausführungspunkten einfach bewerten, speichern und berechnen.

Beispiel für die Verwendung der JavaScript-Konsole mit Aysnc-Aufrufstacks.
Verwende die JavaScript-Konsole in Verbindung mit asynchronen Aufrufstacks, um Fehler im Code zu beheben. Die obige Demo finden Sie hier.

Wenn Sie Ihre Ausdrücke in den Entwicklertools bearbeiten, sparen Sie Zeit, da Sie nicht zum Quellcode zurückwechseln, Änderungen vornehmen oder den Browser aktualisieren müssen.

Verkettung der Lösungsansätze enthüllen

Wenn Sie dachten, der vorherige simulierte Gmail-Ablauf ohne aktivierte asynchrone Aufrufstack-Funktion wäre schwer zu verstehen, können Sie sich vorstellen, wie viel schwieriger es mit komplexeren asynchronen Abläufen wie verketteten Promis wäre? Sehen wir uns noch einmal das letzte Beispiel der Anleitung von Jake Archibald zu JavaScript Promises an.

Die folgende kleine Animation veranschaulicht die Aufrufstacks in Jakes Beispiel async-best-example.html.

Vorher
Haltepunkt in Promise-Beispielen ohne asynchrone Aufrufstacks festgelegt
Der Aufrufstack-Bereich ohne asynchrones Aktivieren.

Beachten Sie, dass im Aufrufstack-Bereich beim Versuch, Promise zu debuggen, nur sehr wenige Informationen enthalten sind.

Nachher
Haltepunkt, der in Promise-Beispielen mit asynchronen Aufrufstacks festgelegt wurde.
Der Aufrufstack-Bereich mit asynchronem Aktivieren.

Wow! Solche Versprechen. Viele Callbacks.

Einblicke in Ihre Webanimationen erhalten

Sehen wir uns die HTML5Rocks-Archive genauer an. Erinnern Sie sich noch an Paul Lewis' Leaner, Meaner, Faster Animations with requestAnimationFrame?

Öffnen Sie die requestAnimationFrame-Demo und fügen Sie einen Haltepunkt am Anfang der Methode update() (um Zeile 874) von post.html ein. Asynchrone Aufrufstacks liefern uns wesentlich mehr Informationen zu requestAnimationFrame, einschließlich der Möglichkeit, bis zum initiierten Scrollereignis-Callback zurückzugehen.

Vorher
Haltepunkt im requestAnimationFrame-Beispiel ohne asynchrone Aufrufstacks festgelegt.
Der Aufrufstack-Bereich ohne asynchrones Aktivieren.
Nachher
Haltepunkt in requestAnimationFrame-Beispiel mit asynchronen Aufrufstacks festgelegt
Und mit asynchronem Aktivieren.

DOM-Aktualisierungen bei Verwendung von MutationObserver finden

Mit MutationObserver können Änderungen im DOM beobachtet werden. In diesem einfachen Beispiel wird beim Klicken auf die Schaltfläche ein neuer DOM-Knoten an <div class="rows"></div> angehängt.

Fügen Sie in demo.html einen Haltepunkt innerhalb von nodeAdded() (Zeile 31) hinzu. Wenn asynchrone Aufrufstacks aktiviert sind, können Sie den Aufrufstack über addNode() zurück zum ersten Klickereignis leiten.

Vorher
Haltepunkt im Beispiel „mutationObserver“ ohne asynchrone Aufrufstacks festgelegt.
Der Aufrufstack-Bereich ohne asynchrones Aktivieren.
Nachher
Haltepunkt im mutationObserver-Beispiel mit asynchronen Aufrufstacks festgelegt
Und mit asynchronem Aktivieren.

Tipps für die Fehlerbehebung in JavaScript in asynchronen Aufrufstacks

Funktionen benennen

Wenn Sie alle Callbacks als anonyme Funktionen zuweisen möchten, können Sie ihnen stattdessen einen Namen geben, um das Aufrufen des Aufrufstacks zu erleichtern.

Nehmen wir als Beispiel eine anonyme Funktion wie die folgende:

window.addEventListener('load', function() {
  // do something
});

Geben Sie ihm einen Namen wie windowLoaded():

window.addEventListener('load', function <strong>windowLoaded</strong>(){
  // do something
});

Wenn das Ladeereignis ausgelöst wird, wird es im Stacktrace der Entwicklertools mit seinem Funktionsnamen anstelle der kryptischen „(anonymous function)“ angezeigt. So können Sie auf einen Blick erkennen, was in Ihrem Stacktrace passiert.

Vorher
Eine anonyme Funktion.
Nachher
Eine benannte Funktion

Weitere Informationen

Zur Erinnerung: Dies sind alle asynchronen Callbacks, in denen die Entwicklertools den vollständigen Aufrufstack anzeigen:

  • Timer: Gehen Sie zurück zur Stelle, an der setTimeout() oder setInterval() initialisiert wurde.
  • XHRs: Gehen Sie zurück zu der Stelle, an der xhr.send() aufgerufen wurde.
  • Animationsframes: Gehen Sie zurück zu der Stelle, an der requestAnimationFrame aufgerufen wurde.
  • Versprechen: Gehe zu der Stelle, an der ein Versprechen aufgelöst wurde.
  • Object.observe: Gehen Sie zurück an die Stelle, an der der Beobachter-Callback ursprünglich gebunden war.
  • MutationObservers: Gehen Sie zurück zur Stelle, an der das Mutationsbeobachter-Ereignis ausgelöst wurde.
  • window.postMessage(): Führt Intra-Prozess-Messaging-Aufrufe durch.
  • DataTransferItem.getAsString()
  • FileSystem API
  • IndexedDB
  • WebSQL
  • Zulässige DOM-Ereignisse über addEventListener(): Gehen Sie zurück zu der Stelle, an der das Ereignis ausgelöst wurde. Aus Leistungsgründen kommen nicht alle DOM-Ereignisse für die Funktion für asynchrone Aufrufstacks infrage. Beispiele für aktuell verfügbare Ereignisse sind „scroll“, „hashchange“ und „selectionchange“.
  • Multimedia-Ereignisse über addEventListener(): Gehen Sie zurück zur Stelle, an der das Ereignis ausgelöst wurde. Verfügbare Multimedia-Ereignisse: Audio- und Videoereignisse (z.B. "play", "pause", "ratechange"), WebRTC MediaStreamTrackList-Ereignisse (z.B. "addtrack", "removetrack") und MediaSource-Ereignisse (z.B. "sourceopen").

Sie können den vollständigen Stacktrace Ihrer JavaScript-Callbacks sehen. Diese Funktion in den Entwicklertools ist besonders hilfreich, wenn mehrere asynchrone Ereignisse miteinander in Beziehung stehen oder wenn eine nicht abgefangene Ausnahme aus einem asynchronen Callback ausgelöst wird.

Probier es in Chrome aus. Wenn Sie Feedback zu dieser neuen Funktion haben, schreiben Sie uns über den Bug-Tracker der Chrome-Entwicklertools oder in der Gruppe „Chrome DevTools“.