Debug di JavaScript asincrono con Chrome DevTools

Pearl Chen

Introduzione

Una potente funzionalità che rende unico JavaScript è la capacità di funzionare in modo asincrono tramite le funzioni di callback. L'assegnazione di callback asincroni ti consente di scrivere codice basato sugli eventi, ma rende anche il rilevamento dei bug un'esperienza di strappo, dal momento che JavaScript non viene eseguito in modo lineare.

Fortunatamente, ora in Chrome DevTools puoi visualizzare completo lo stack di chiamate dei callback JavaScript asincroni.

Una rapida panoramica degli stack di chiamate asincrone.
Un rapido teaser per una panoramica degli stack di chiamate asincrone. A breve annulleremo il flusso di questa demo.

Dopo aver abilitato la funzionalità dello stack di chiamate asincrone in DevTools, potrai visualizzare in dettaglio lo stato della tua app web in vari momenti. Esegui l'analisi completa degli stack per alcuni listener di eventi, setInterval,setTimeout, XMLHttpRequest, promise, requestAnimationFrame, MutationObservers e altri.

Mentre esegui l'analisi dello stack, puoi anche analizzare il valore di qualsiasi variabile in quel particolare punto dell'esecuzione del runtime. È come una macchina del tempo per le espressioni 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 Stack chiamate, sul lato destro, è presente una nuova casella di controllo per "Async". Seleziona la casella di controllo per attivare o disattivare il debug asincrono. Anche se una volta attivato, potresti non volerlo mai disattivare.

Attiva o disattiva la funzionalità asincrona.

Acquisisci eventi timer ritardati e risposte XHR

Probabilmente hai già notato quanto segue in Gmail:

Nuovo tentativo di Gmail di inviare un'email.

Se si verifica un problema durante l'invio della richiesta (o il server ha problemi di connettività di rete sul lato client), Gmail proverà automaticamente a inviare di nuovo il messaggio dopo un breve timeout.

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

Diagramma di flusso dell'esempio di Gmail simulato.
Nel diagramma sopra, i metodi evidenziati in blu sono i punti chiave in cui questa nuova funzionalità di DevTools è più vantaggiosa perché questi metodi funzionano in modo asincrono.

Osservando esclusivamente il riquadro Stack chiamate nelle versioni precedenti di DevTools, un punto di interruzione all'interno di postOnFail() fornirebbe poche informazioni sulla provenienza della chiamata di postOnFail(). Ma guarda la differenza quando attivi gli stack asincroni:

Prima
Punto di interruzione impostato nell'esempio fittizio di Gmail senza stack di chiamate asincroni.
Il riquadro Stack chiamate senza asincrono attivato.

Qui puoi vedere che postOnFail() è stato avviato da un callback AJAX, ma non ci sono ulteriori informazioni.

Dopo
Punto di interruzione impostato nell'esempio fittizio di Gmail con stack di chiamate asincroni.
Il riquadro Stack chiamate con la modalità asincrona attivata.

Qui puoi vedere che l'XHR è stato avviato da submitHandler(). Bene.

Con gli stack di chiamate asincroni attivati, puoi visualizzare l'intero stack di chiamate per vedere facilmente se la richiesta è stata avviata da submitHandler() (che avviene dopo aver fatto clic sul pulsante Invia) o da retrySubmit() (che avviene dopo un ritardo di setTimeout()):

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

Controlla le espressioni in modo asincrono

Quando percorri l'intero stack di chiamate, anche le espressioni controllate verranno aggiornate per riflettere lo stato in cui si trovavano in quel momento.

Esempio di utilizzo delle espressioni di visualizzazione con gli stack di chiamate aysnc

Valuta il codice dagli ambiti precedenti

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

Immagina di essere il dottor Who e di aver bisogno di un po' di aiuto per confrontare l'orologio del passato con il Tardis "oggi". Dalla console DevTools puoi facilmente valutare, archiviare ed eseguire calcoli sui valori provenienti da diversi punti di esecuzione.

Esempio di utilizzo della console JavaScript con gli stack di chiamate aysnc.
Utilizza la console JavaScript insieme agli stack di chiamate asincrone per eseguire il debug del codice. La demo sopra riportata è disponibile qui.

Se rimani all'interno di DevTools per manipolare le espressioni, non dovrai più tornare al codice sorgente, apportare modifiche e aggiornare il browser.

Svela la risoluzione delle promesse incatenate

Se ritieni che il precedente flusso fittizio di Gmail sia difficile da svelare senza la funzionalità asincrona dello stack di chiamate, puoi immaginare quanto sarebbe più difficile con flussi asincroni più complessi come le promesse concatenate? Rivediamo l'ultimo esempio del tutorial di Jake Archibald su JavaScript Promises.

Di seguito è riportata un'animazione che mostra un'animazione degli stack di chiamate nell'esempio async-best-example.html di Jake.

Prima
Punto di interruzione impostato in esempio di promesse senza stack di chiamate asincroni
Il riquadro Stack chiamate senza asincrono attivato.

Nota che il riquadro Stack di chiamate fornisce informazioni piuttosto brevi quando provi a eseguire il debug delle promesse.

Dopo
Punto di interruzione impostato in un esempio di promesse con stack di chiamate asincroni.
Il riquadro Stack chiamate con la modalità asincrona attivata.

Complimenti. Promesse del genere. Molti richiami.

Ottieni informazioni sulle tue animazioni web

Analizziamo più in dettaglio gli archivi di HTML5Rocks. Ricordi il video di Paul Lewis Leaner, Meaner, Faster Animations with requestAnimationFrame?

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

Prima
Punto di interruzione impostato nell'esempio requestAnimationFrame senza stack di chiamate asincroni.
Il riquadro Stack chiamate senza asincrono attivato.
Dopo
Punto di interruzione impostato nell'esempio requestAnimationFrame con stack di chiamate asincroni
E con la modalità asincrona attivata.

Rileva gli aggiornamenti del DOM quando utilizzi MutationObservationr

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

Aggiungi un punto di interruzione all'interno di nodeAdded() (riga 31) in demo.html. Con gli stack di chiamate asincroni abilitati, ora puoi riportare lo stack di chiamate tramite addNode() all'evento di clic iniziale.

Prima
Punto di interruzione impostato nell&#39;esempio mutationObservationr senza stack di chiamate asinc.
Il riquadro Stack chiamate senza asincrono attivato.
Dopo
Punto di interruzione impostato nell&#39;esempio mutationObservationr con stack di chiamate asincrone.
E con la modalità asincrona attivata.

Suggerimenti per il debug di JavaScript negli stack di chiamate asincrone

Assegna un nome alle funzioni

Se tendi ad assegnare tutti i callback come funzioni anonime, puoi assegnare loro un nome per semplificare la visualizzazione dello stack di chiamate.

Prendiamo ad esempio una funzione anonima come la seguente:

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

Assegna un nome, ad esempio windowLoaded():

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

Quando si attiva, l'evento di caricamento viene visualizzato nell'analisi dello stack DevTools con il relativo nome della funzione anziché con la criptica "(funzione anonima)". In questo modo è molto più facile vedere a colpo d'occhio cosa sta succedendo nell'analisi dello stack.

Prima
Una funzione anonima.
Dopo il giorno
Una funzione con nome

Per saperne di più

Ricapitolando, questi sono tutti i callback asincroni in cui DevTools mostrerà l'intero stack di chiamate:

  • Timer: Torna al punto in cui è stato inizializzato setTimeout() o setInterval().
  • XHR: Torna al punto in cui è stato chiamato xhr.send().
  • Frame animazioni: torna al punto in cui è stato chiamato requestAnimationFrame.
  • Promesse: Torna al punto in cui una promessa è stata risolta.
  • Object.observe: torna al punto in cui era originariamente associato il callback di osservatore.
  • MutationObservers: Torna nel punto in cui è stato attivato l'evento osservatore mutazione.
  • window.postMessage(): consente di eseguire l'accesso alle chiamate di messaggistica intra-process.
  • DataTransferItem.getAsString()
  • API FileSystem
  • IndexedDB
  • WebSQL
  • Eventi DOM idonei tramite addEventListener(): torna al luogo in cui l'evento è stato attivato. Per motivi legati alle prestazioni, non tutti gli eventi DOM sono idonei per la funzionalità degli stack di chiamate asincroni. Alcuni esempi di eventi attualmente disponibili sono: "scroll", "hashchange" e "selectionchange".
  • Eventi multimediali tramite addEventListener(): torna dove è 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").

Riuscire a vedere l'analisi completa dello stack dei callback JavaScript dovrebbe mantenere quei capelli in testa. Questa funzionalità in DevTools sarà particolarmente utile quando si verificano più eventi asincroni l'uno rispetto all'altro o se viene generata un'eccezione non rilevata da un callback asincrono.

Provalo in Chrome. Se hai feedback su questa nuova funzionalità, scrivici nel tracker dei bug di Chrome DevTools o nel gruppo di Chrome DevTools.