Debug di JavaScript asincrono con Chrome DevTools

Pearl Chen

Introduzione

Una funzionalità potente che rende JavaScript unico è la sua capacità di lavorare in modo asincrono tramite funzioni di callback. L'assegnazione di callback asincroni ti consente di scrivere codice basato su eventi, ma rende anche il rilevamento dei bug un'esperienza frustrante, poiché il codice JavaScript non viene eseguito in modo lineare.

Fortunatamente, ora in Chrome DevTools puoi visualizzare la totalità dello stack di chiamate dei callback JavaScript asincroni.

Una breve panoramica degli stack di chiamate asincrone.
Una breve panoramica degli stack di chiamate asincrone. (A breve analizzeremo il flusso di questa demo).

Una volta attivata la funzionalità dello stack di chiamate asincrone in DevTools, potrai esaminare in dettaglio lo stato della tua app web in vari momenti. Esplora la traccia completa dello stack per alcuni ascoltatori di eventi, setInterval,setTimeout, XMLHttpRequest, promesse, requestAnimationFrame, MutationObservers e altro ancora.

Durante l'esplorazione della traccia dello stack, puoi anche analizzare il valore di qualsiasi variabile in quel determinato punto di esecuzione del runtime. È come una macchina del tempo per le espressioni del tuo smartwatch.

Attiviamo questa funzionalità e diamo un'occhiata ad alcuni di questi scenari.

Attivare il debug asincrono in Chrome

Prova questa nuova funzionalità attivandola in Chrome. Vai al riquadro Origini di Chrome Canary DevTools.

Accanto al riquadro Call Stack sul lato destro, è presente una nuova casella di controllo per "Asincrono". Attiva o disattiva la casella di controllo per attivare o disattivare il debug asincrono. Tuttavia, una volta attivata, potresti non volerla disattivare mai.

Attiva o disattiva la funzionalità asincrona.

Acquisisci eventi timer ritardati e risposte XHR

Probabilmente l'hai già visto in Gmail:

Gmail sta tentando di nuovo di inviare un'email.

Se si verifica un problema di invio della richiesta (il server ha problemi o si verificano problemi di connettività di rete lato client), Gmail tenterà automaticamente di inviare di nuovo il messaggio dopo un breve timeout.

Per capire in che modo gli stack di chiamate asincrone possono aiutarci ad analizzare gli eventi timer ritardati e le risposte XHR, ho ricreato questo flusso con un esempio simulato di Gmail. Il codice JavaScript completo è disponibile al link riportato sopra, ma il flusso è il seguente:

Diagramma di flusso di un esempio di Gmail simulato.
Nel diagramma sopra, i metodi evidenziati in blu sono i punti di forza di questa nuova funzionalità di DevTool, in quanto sono i più vantaggiosi poiché funzionano in modo asincrono.

Se guardi solo il riquadro Stack di chiamate nelle versioni precedenti di DevTools, un breakpoint all'interno di postOnFail() fornisce poche informazioni su dove viene chiamato postOnFail(). Ma guarda la differenza quando attivi le serie asincrone:

Prima
Punto di interruzione impostato nell'esempio di Gmail simulato senza stack di chiamate asincrone.
Il riquadro Call Stack senza l'abilitazione dell'esecuzione asincrona.

Qui puoi vedere che postOnFail() è stato avviato da una chiamata AJAX, ma non sono disponibili ulteriori informazioni.

Dopo
Punto di interruzione impostato nell'esempio di Gmail simulato con stack di chiamate asincrone.
Il riquadro Stack di chiamate con l'opzione asincrona attivata.

Qui puoi vedere che la richiesta XHR è stata avviata da submitHandler(). Bene!

Con le serie di chiamate asincrone attivate, puoi visualizzare l'intera serie di chiamate per vedere facilmente se la richiesta è stata avviata da submitHandler() (dopo aver fatto clic sul pulsante Invia) o da retrySubmit() (dopo un ritardo di setTimeout()):

submitHandler()
Punto di interruzione impostato nell'esempio di Gmail simulato con stack di chiamate asincrone
retrySubmit()
Un altro punto di interruzione impostato nell'esempio di Gmail simulato con stack di chiamate asincrone

Espressioni di controllo asincrone

Quando esamini l'intero stack di chiamate, anche le espressioni monitorate si aggiorneranno per riflettere lo stato in quel momento.

Un esempio di utilizzo delle espressioni di monitoraggio con gli stack di chiamate asincrone

Valutare il codice da ambiti precedenti

Oltre a osservare semplicemente le espressioni, puoi interagire con il codice degli ambiti precedenti direttamente nel riquadro della console JavaScript di DevTools.

Immagina di essere il Dr. Who e di avere bisogno di un piccolo aiuto per confrontare l'orologio di prima di salire nella Tardis con quello di "ora". Dalla console di DevTools puoi valutare, memorizzare ed eseguire facilmente calcoli sui valori di diversi punti di esecuzione.

Un esempio di utilizzo della console JavaScript con stack di chiamate asincrone.
Utilizza la console JavaScript in combinazione con gli stack di chiamate asincrone per eseguire il debug del codice. La demo sopra riportata è disponibile qui.

Se continui a utilizzare DevTools per manipolare le espressioni, risparmierai tempo evitando di dover tornare al codice sorgente, apportare modifiche e aggiornare il browser.

Scoprire le risoluzioni delle promesse concatenate

Se pensavi che il precedente flusso simulato di Gmail fosse difficile da comprendere senza la funzionalità di elaborazione della pila di chiamate asincrona abilitata, puoi immaginare quanto sarebbe più difficile con flussi asincroni più complessi come le promesse incatenate? Esaminiamo di nuovo l'ultimo esempio del tutorial di Jake Archibald sulle promesse JavaScript.

Ecco una piccola animazione dell'esplorazione delle pile di chiamate nell'esempio async-best-example.html di Jake.

Prima
Punto di interruzione impostato nell'esempio di promesse senza stack di chiamate asincrone
Il riquadro Call Stack senza l'abilitazione dell'esecuzione asincrona.

Nota che il riquadro Stack di chiamate contiene informazioni piuttosto brevi quando si tenta di eseguire il debug delle promesse.

Dopo
Punto di interruzione impostato nell'esempio di promesse con stack di chiamate asincrone.
Il riquadro Stack di chiamate con l'opzione asincrona attivata.

Wow! Che promesse. Molti callback.

Ottenere informazioni sulle animazioni web

Andiamo più a fondo negli archivi di HTML5Rocks. Ricordi l'articolo di Paul Lewis Animazioni più snelle, più efficaci e più veloci con requestAnimationFrame?

Apri la demo di requestAnimationFrame e aggiungi un punto di interruzione all'inizio del metodo update() (intorno alla riga 874) di post.html. Con gli stack di chiamate asincrone otteniamo molti più approfondimenti su requestAnimationFrame, inclusa la possibilità di risalire fino al callback dell'evento di scorrimento iniziale.

Prima
Punto di interruzione impostato nell'esempio di requestAnimationFrame senza stack di chiamate asincrone.
Il riquadro Call Stack senza l'abilitazione dell'esecuzione asincrona.
Dopo
Punto di interruzione impostato nell'esempio di requestAnimationFrame con stack di chiamate asincrone
E con l'aggiornamento asincrono abilitato.

Individuare gli aggiornamenti del DOM quando si utilizza MutationObserver

MutationObserver ci consentono di osservare le modifiche nel DOM. In questo semplice esempio, quando fai clic sul pulsante, a <div class="rows"></div> viene aggiunto un nuovo nodo DOM.

Aggiungi un punto di interruzione in nodeAdded() (riga 31) in demo.html. Con le strutture di chiamate asincrone attivate, ora puoi eseguire la procedura inversa nella struttura di chiamate fino a addNode() per arrivare all'evento di clic iniziale.

Prima
Punto di interruzione impostato nell&#39;esempio di mutationObserver senza stack di chiamate asincrone.
Il riquadro Call Stack senza l'abilitazione dell'esecuzione asincrona.
Dopo
Punto di interruzione impostato nell&#39;esempio di mutationObserver con stack di chiamate asincrone.
E con l'aggiornamento asincrono abilitato.

Suggerimenti per il debug di JavaScript negli stack di chiamate asincrone

Assegna un nome alle funzioni

Se tendi ad assegnare tutti i tuoi callback come funzioni anonime, ti consigliamo di assegnare loro un nome per facilitare la visualizzazione dello stack delle chiamate.

Ad esempio, prendi una funzione anonima come questa:

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

Assegna un nome, ad esempio windowLoaded():

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

Quando viene attivato l'evento load, viene visualizzato nella traccia dello stack di DevTools con il nome della funzione anziché con l'enigmatica "(funzione anonima)". In questo modo, è molto più facile vedere a colpo d'occhio cosa sta succedendo nella traccia dello stack.

Prima
Una funzione anonima.
Dopo
Una funzione con nome

Per saperne di più

Per riepilogare, questi sono tutti i callback asincroni in cui DevTools mostrerà lo stack di chiamate completo:

  • Timer: riprendi da dove è stato inizializzato setTimeout() o setInterval().
  • Richieste XHR: Torna al punto in cui è stata chiamata xhr.send().
  • Frame di animazione: riprendi da dove è stata chiamata requestAnimationFrame.
  • Promesse: riprendi da dove è stata risolta una promessa.
  • Object.observe: Torna al punto in cui è stato originariamente associato il callback dell'osservatore.
  • MutationObservers: Torna al punto in cui è stato attivato l'evento MutationObserver.
  • window.postMessage(): esamina le chiamate di messaggistica all'interno del processo.
  • DataTransferItem.getAsString()
  • API FileSystem
  • IndexedDB
  • WebSQL
  • Eventi DOM idonei tramite addEventListener(): Torna al punto in cui è stato attivato l'evento. Per motivi di prestazioni, non tutti gli eventi DOM sono idonei per la funzionalità degli stack di chiamate asincrone. Alcuni esempi di eventi attualmente disponibili sono: "scroll", "hashchange" e 'selectionchange'.
  • Eventi multimediali tramite addEventListener(): Torna al punto in cui è stato attivato l'evento. Gli eventi multimediali disponibili includono: eventi audio e video (ad es. "play", "pause", "ratechange"), eventi WebRTC MediaStreamTrackList (ad es. "addtrack", "removetrack") ed eventi MediaSource (ad es. "sourceopen").

La possibilità di visualizzare la traccia completa dello stack dei callback JavaScript dovrebbe aiutarti a mantenere la calma. Questa funzionalità di DevTools sarà particolarmente utile quando si verificano più eventi asincroni in relazione tra loro o se viene lanciata un'eccezione non rilevata all'interno di un callback asincrono.

Prova a utilizzarlo in Chrome. Se vuoi fornire feedback su questa nuova funzionalità, scrivici nel tracker dei bug di Chrome DevTools o nel gruppo Chrome DevTools.