Einführung
Eine leistungsstarke Funktion, die JavaScript einzigartig macht, ist die Möglichkeit, asynchron über Callback-Funktionen zu arbeiten. Durch das Zuweisen von asynchronen Callbacks können Sie ereignisgesteuerten Code schreiben. Das Aufspüren von Fehlern ist jedoch sehr mühsam, da der JavaScript-Code nicht linear ausgeführt wird.
Glücklicherweise können Sie sich jetzt in den Chrome-Entwicklertools den vollständigen Aufrufstapel von asynchronen JavaScript-Callbacks ansehen.
Sobald Sie die Funktion „Async Call Stack“ in den DevTools aktiviert haben, können Sie den Status Ihrer Webanwendung zu verschiedenen Zeitpunkten untersuchen. Vollständigen Stack-Trace für einige Ereignis-Listener, setInterval
, setTimeout
, XMLHttpRequest
, Versprechen, requestAnimationFrame
, MutationObservers
und mehr aufrufen
Beim Durchlaufen des Stack-Traces können Sie auch den Wert einer beliebigen Variablen an diesem bestimmten Punkt der Laufzeitausführung analysieren. Es ist wie eine Zeitmaschine für Ihre Zifferblätter.
Aktivieren wir diese Funktion und sehen uns einige dieser Szenarien an.
Async-Fehlerbehebung in Chrome aktivieren
Sie können diese neue Funktion in Chrome aktivieren und ausprobieren. Rufen Sie in den Chrome Canary-Entwicklertools den Bereich Quellen auf.
Rechts neben dem Bereich Call Stack (Anrufstapel) befindet sich ein neues Kästchen für „Async“ (Asynchron). Aktivieren oder deaktivieren Sie das Kästchen, um das asynchrone Debuggen zu aktivieren oder zu deaktivieren. Wenn Sie es einmal aktiviert haben, sollten Sie es jedoch nicht mehr deaktivieren.
Verzögerte Timer-Ereignisse und XHR-Antworten erfassen
Das haben Sie in Gmail wahrscheinlich schon einmal gesehen:
Wenn beim Senden der Anfrage ein Problem auftritt (entweder auf dem Server oder auf der Clientseite aufgrund von Netzwerkverbindungsproblemen), versucht Gmail nach einer kurzen Zeitüberschreitung automatisch, die Nachricht noch einmal zu senden.
Um zu sehen, wie asynchrone Aufrufstapel uns bei der Analyse verzögerter Timerereignisse und XHR-Antworten helfen können, habe ich diesen Ablauf mit einem Mock-Gmail-Beispiel nachgestellt. Den vollständigen JavaScript-Code findest du über den Link oben. Der Ablauf ist jedoch so:
In früheren Versionen von DevTools gab ein Haltepunkt in postOnFail()
nur wenig Aufschluss darüber, woher postOnFail()
aufgerufen wurde. Sehen Sie sich aber den Unterschied an, wenn Sie asynchrone Stacks aktivieren:
Wenn asynchrone Aufrufstacks aktiviert sind, können Sie den gesamten Aufrufstack aufrufen, um leicht zu erkennen, ob die Anfrage von submitHandler()
(nach dem Klicken auf die Schaltfläche „Senden“) oder von retrySubmit()
(nach einer Verzögerung von setTimeout()
) gestartet wurde:
Watch-Ausdrücke asynchron auswerten
Wenn Sie den gesamten Aufrufstapel durchgehen, werden auch Ihre beobachteten Ausdrücke aktualisiert, um den Status zu diesem Zeitpunkt widerzuspiegeln.
Code aus früheren Bereichen auswerten
Sie können Ausdrücke nicht nur beobachten, sondern auch direkt in der JavaScript-Konsole der Entwicklertools mit Ihrem Code aus vorherigen Bereichen interagieren.
Stellen Sie sich vor, Sie sind Dr. Who und benötigen ein wenig Hilfe beim Vergleichen der Uhr von vor dem Einsteigen in die Tardis mit der Uhrzeit „jetzt“. In der DevTools-Konsole können Sie Werte aus verschiedenen Ausführungspunkten ganz einfach auswerten, speichern und berechnen.
Wenn Sie Ihre Ausdrücke in den Entwicklertools bearbeiten, sparen Sie Zeit, da Sie nicht zum Quellcode zurückkehren, Änderungen vornehmen und den Browser aktualisieren müssen.
Verkettete Promise-Auflösungen entwirren
Wenn Sie dachten, dass der vorherige Mock-Gmail-Vorgang ohne aktivierte Funktion für den asynchronen Aufrufstapel schwer zu entwirren war, können Sie sich vorstellen, wie viel schwieriger es bei komplexeren asynchronen Abläufen wie verketteten Versprechen wäre? Sehen wir uns noch einmal das letzte Beispiel aus Jake Archibalds Anleitung zu JavaScript-Versprechen an.
Hier ist eine kleine Animation, die die Aufrufsstacks in Jakes Beispiel async-best-example.html durchläuft.
Statistiken zu Webanimationen abrufen
Sehen wir uns die HTML5Rocks-Archive genauer an. Erinnern Sie sich an Paul Lewis' Leaner, Meaner, Faster Animations with requestAnimationFrame?
Öffnen Sie die Demo für requestAnimationFrame und fügen Sie am Anfang der Methode update() (etwa in Zeile 874) von post.html einen Haltepunkt hinzu. Mit asynchronen Aufrufstacks erhalten wir viel mehr Informationen zu requestAnimationFrame, einschließlich der Möglichkeit, bis zum Callback des einleitenden Scrollereignisses zurückzuverfolgen.
DOM-Änderungen bei Verwendung von MutationObserver ermitteln
MutationObserver
ermöglichen es uns, Änderungen im DOM zu beobachten. In diesem einfachen Beispiel wird durch Klicken auf die Schaltfläche <div class="rows"></div>
ein neuer DOM-Knoten angehängt.
Fügen Sie in demo.html in nodeAdded()
(Zeile 31) einen Haltepunkt hinzu. Wenn asynchrone Aufrufstacks aktiviert sind, können Sie den Aufrufstapel jetzt über addNode()
bis zum ursprünglichen Klickereignis zurückverfolgen.
Tipps zur Fehlerbehebung bei JavaScript in asynchronen Aufrufstapeln
Funktionen benennen
Wenn Sie alle Callbacks als anonyme Funktionen zuweisen, sollten Sie ihnen einen Namen geben, damit der Aufrufstack leichter zu lesen ist.
Hier ein Beispiel für eine anonyme Funktion:
window.addEventListener('load', function() {
// do something
});
Geben Sie ihm einen Namen wie windowLoaded()
:
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
Wenn das Ereignis „load“ ausgelöst wird, wird es im DevTools-Stack-Trace mit dem Funktionsnamen anstelle des kryptischen „(anonyme Funktion)“ angezeigt. So lässt sich auf einen Blick leichter erkennen, was im Stack-Trace passiert.
Weitere Informationen
Hier noch einmal alle asynchronen Rückrufe, bei denen in DevTools der vollständige Aufrufstapel angezeigt wird:
- Timer:
Gehen Sie zurück zu dem Punkt, an dem
setTimeout()
odersetInterval()
initialisiert wurde. - XHRs: Gehen Sie zurück zu dem Punkt, an dem
xhr.send()
aufgerufen wurde. - Animationsframes: Gehen Sie zurück zu der Stelle, an der
requestAnimationFrame
aufgerufen wurde. - Versprechen: Gehen Sie zurück zu der Stelle, an der ein Versprechen aufgelöst wurde.
- Object.observe: Gehen Sie zurück zu der Stelle, an der der Beobachter-Callback ursprünglich gebunden wurde.
- MutationObservers: Gehen Sie zurück zu dem Punkt, an dem das Mutation Observer-Ereignis ausgelöst wurde.
- window.postMessage(): Durchlaufen von Aufrufen von intraprozeduralen Messaging-Funktionen.
- DataTransferItem.getAsString()
- FileSystem API
- IndexedDB
- WebSQL
- Zulässige DOM-Ereignisse über
addEventListener()
: Gehen Sie zurück zum Ort, an dem das Ereignis ausgelöst wurde. Aus Leistungsgründen sind nicht alle DOM-Ereignisse für die Funktion „Asynchroner Aufrufstapel“ geeignet. Beispiele für derzeit verfügbare Ereignisse sind „scroll“, „hashchange“ und „selectionchange“. - Multimedia-Ereignisse über
addEventListener()
: Gehen Sie zurück zum Ursprung des Ereignisses. Zu den verfügbaren Multimedia-Ereignissen gehören Audio- und Videoereignisse (z.B. „play“, „pause“, „ratechange“), WebRTC MediaStreamTrackList-Ereignisse (z.B. „addtrack“, „removetrack“) und MediaSource-Ereignisse (z.B. „sourceopen“).
Wenn Sie den vollständigen Stack-Trace Ihrer JavaScript-Callbacks sehen können, sollten Sie Ihre Haare behalten. Diese Funktion in den DevTools ist besonders hilfreich, wenn mehrere asynchrone Ereignisse in Bezug zueinander auftreten oder wenn eine nicht aufgefangene Ausnahme innerhalb eines asynchronen Rückrufs ausgelöst wird.
Probieren Sie es in Chrome aus. Wenn Sie Feedback zu dieser neuen Funktion haben, können Sie uns dies im Fehler-Tracker der Chrome-Entwicklertools oder in der Chrome-Entwicklertools-Gruppe mitteilen.