Debugowanie asynchronicznego kodu JavaScript za pomocą Narzędzi deweloperskich w Chrome

Wstęp

Ważną cechą, która sprawia, że JavaScript jest niepowtarzalny, jest możliwość asynchronicznej pracy za pomocą funkcji wywołań zwrotnych. Przypisywanie asynchronicznych wywołań zwrotnych pozwala pisać kod oparty na zdarzeniach, ale sprawia też, że śledzenie błędów sprawia, że JavaScript nie jest wykonywany w sposób liniowy.

Na szczęście w Narzędziach deweloperskich w Chrome możesz wyświetlić pełny stos wywołań zwrotnych asynchronicznych wywołań JavaScriptu.

Krótki zwiastun dotyczący asynchronicznych stosów wywołań.
Krótki zwiastun dotyczący asynchronicznych stosów wywołań. (wkrótce omówimy schemat działania tej wersji demonstracyjnej).

Po włączeniu funkcji asynchronicznego stosu wywołań w Narzędziach deweloperskich będziesz mieć możliwość analizowania stanu aplikacji internetowej w różnych momentach. Przejdź pełny zrzut stosu w przypadku niektórych detektorów zdarzeń: setInterval, setTimeout, XMLHttpRequest, obietnic, requestAnimationFrame, MutationObservers i innych.

W trakcie zrzutu stosu możesz też analizować wartość dowolnej zmiennej w konkretnym punkcie wykonywania zrzutu stosu. To jak wehikuł czasu dla wyrażeń zegarka.

Włączmy tę funkcję i przyjrzyjmy się kilku przykładom.

Włączanie debugowania asynchronicznego w Chrome

Wypróbuj tę nową funkcję w Chrome. Otwórz panel Źródła w Narzędziach deweloperskich w Chrome Canary.

Obok panelu Stos wywołań po prawej stronie znajduje się nowe pole wyboru „Asynchroniczny”. Włącz lub wyłącz debugowanie asynchroniczne (chociaż gdy jest ono włączone, możesz w ogóle nie chcieć go wyłączać).

Włącz lub wyłącz funkcję asynchroniczną.

Rejestruj zdarzenia opóźnionego licznika i odpowiedzi XHR

Prawdopodobnie zdarzyło Ci się już widzieć taką wiadomość w Gmailu:

Gmail próbuje wysłać e-maila na nowo.

Jeśli wystąpi problem z wysłaniem żądania (np. wystąpią problemy z serwerem lub połączenie z siecią po stronie klienta), Gmail automatycznie spróbuje ponownie wysłać wiadomość po krótkim czasie oczekiwania.

Aby zobaczyć, jak asynchroniczne stosy wywołań mogą pomóc nam analizować zdarzenia opóźnionego licznika czasu i odpowiedzi XHR, odtworzyłem ten proces za pomocą przykładowego przykładu z Gmaila. Pełny kod JavaScript znajdziesz, klikając powyższy link, ale wygląda to tak:

Schemat blokowy przedstawiający przykładową aplikację Gmail.
Na diagramie powyżej metody zaznaczone na niebiesko to najlepsze pozycje, dla których nowa funkcja DevTool jest najbardziej korzystna, ponieważ działają one asynchronicznie.

Jeśli spojrzysz wyłącznie na panel Stos wywołań w poprzednich wersjach Narzędzi deweloperskich, punkt przerwania w postOnFail() będzie zawierał niewiele informacji o sposobie wywołania postOnFail(). Zwróć jednak uwagę na różnicę, gdy włączysz stosy asynchroniczne:

Przed
Punkt przerwania ustawiony w przykładowej wersji Gmaila bez asynchronicznych stosów wywołań.
Panel stosu wywołań bez włączonej asynchronicznej.

Widać, że interfejs postOnFail() został zainicjowany z wywołania zwrotnego AJAX, ale nie ma dodatkowych informacji.

Po
Punkt przerwania ustawiony w przykładowej wersji Gmaila z asynchronicznymi stosami wywołań.
Panel stosu wywołań z włączoną asynchronizacją.

Widać, że XHR zostało zainicjowane przez submitHandler(). Super!

Gdy włączone są asynchroniczne stosy wywołań, możesz wyświetlić cały stos wywołań, aby z łatwością sprawdzić, czy żądanie zostało zainicjowane z submitHandler() (co następuje po kliknięciu przycisku przesyłania) czy z retrySubmit() (co następuje po setTimeout() opóźnieniu):

submitHandler()
Punkt przerwania ustawiony w przykładowej wersji Gmaila z asynchronicznymi stosami wywołań
retrySubmit()
Kolejny punkt przerwania ustawiony w przykładowej wiadomości Gmaila z asynchronicznymi stosami wywołań

Asynchronicznie obserwuj wyrażenia

Podczas spaceru po pełnym stosie wywołań również obserwowane wyrażenia zostaną zaktualizowane, aby odzwierciedlić stan w danym momencie.

Przykład użycia wyrażeń obserwacyjnych ze stosami wywołań Aysnc

Oceń kod z wcześniejszych zakresów

Oprócz prostych obserwacji wyrażeń możesz też wchodzić w interakcję z kodem z poprzednich zakresów bezpośrednio w panelu konsoli JavaScript Narzędzi dla programistów.

Wyobraź sobie, że jesteś Doktorem Who i potrzebujesz pomocy w porównaniu zegara z czasem przed przybyciem do Tardis i „teraz”. W konsoli Narzędzi deweloperskich możesz w łatwy sposób oceniać i przechowywać wartości z różnych punktów wykonywania oraz wykonywać na nich obliczenia.

Przykład użycia konsoli JavaScript ze stosami wywołań aysnc.
Do debugowania kodu używaj konsoli JavaScript w połączeniu z asynchronicznymi stosami wywołań. Powyższą wersję demonstracyjną znajdziesz tutaj.

Pozostawanie w narzędziach deweloperskich do manipulowania wyrażeniami pozwoli Ci zaoszczędzić czas, ponieważ nie musisz wracać do kodu źródłowego, wprowadzać zmiany i odświeżać przeglądarkę.

Odkryj łańcuchowe postanowienia dotyczące obietnic

Myślisz, że poprzednia przykładowa procedura w Gmailu nie jest łatwa do opanowania bez włączonej funkcji asynchronicznego stosu wywołań? Wyobraź sobie, o ile trudniej byłoby to zrobić w przypadku bardziej złożonych procesów asynchronicznych, takich jak łańcuchowe obietnice? Powtórzmy ostatni przykład samouczka Jake'a Archibalda na temat obietnic JavaScriptu.

Przyjrzyjmy się, jak przebiegają stosy wywołań w przykładzie async-best-example.html Jake'a.

Przed
Punkt przerwania ustawiony w przykładowym obiecu bez asynchronicznych stosów wywołań
Panel stosu wywołań bez włączonej asynchronicznej.

Zwróć uwagę, że panel Stosów wywołań zawiera za mało informacji podczas debugowania obietnic.

Po
Punkt przerwania ustawiony w przykładowym obiecu z asynchronicznymi stosami wywołań.
Panel stosu wywołań z włączoną asynchronizacją.

Niesamowite! Takie obietnice. Wiele wywołań zwrotnych.

Uzyskaj wgląd w swoje animacje internetowe

Przyjrzyjmy się bliżej archiwam HTML5Rocks. Pamiętasz prowadzoną przez Paula Lewisa funkcję Leaner, Meaner, Accelerated Animations with requestAnimationFrame?

Otwórz demonstrację elementu requestAnimationFrame i dodaj punkt przerwania na początku metody update() (w okolicach wiersza 874) posta.html. Asynchroniczne stosy wywołań uzyskują znacznie więcej informacji o funkcji requestAnimationFrame, w tym możliwość przejścia z powrotem do inicjowania wywołania zwrotnego zdarzenia przewijania.

Przed
Punkt przerwania ustawiony w przykładzie requestAnimationFrame bez asynchronicznych stosów wywołań.
Panel stosu wywołań bez włączonej asynchronicznej.
Po
Punkt przerwania ustawiony w przykładzie requestAnimationFrame z asynchronicznymi stosami wywołań
z włączoną asynchronizacją.

Śledzenie aktualizacji DOM podczas korzystania z MutationObserver

MutationObserver umożliwiają obserwowanie zmian w DOM. W tym prostym przykładzie, gdy klikniesz przycisk, do <div class="rows"></div> zostanie dołączony nowy węzeł DOM.

Dodaj punkt przerwania w obrębie nodeAdded() (wiersz 31) w demo.html. Po włączeniu stosu wywołań asynchronicznych możesz teraz przeprowadzić grupę wywołań z powrotem przez addNode() do zdarzenia początkowego kliknięcia.

Przed
Punkt przerwania ustawiony w przykładzie mutationObserver bez asynchronicznych stosów wywołań.
Panel stosu wywołań bez włączonej asynchronicznej.
Po
Punkt przerwania ustawiony w przykładzie z mutationObserver z asynchronicznymi stosami wywołań.
z włączoną asynchronizacją.

Wskazówki dotyczące debugowania kodu JavaScript w asynchronicznych stosach wywołań

Nazywanie funkcji

Jeśli zazwyczaj przypisujesz wszystkie wywołania zwrotne jako funkcje anonimowe, lepiej nadać im nazwy, aby ułatwić przeglądanie stosu wywołań.

Weźmy na przykład taką anonimową funkcję:

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

I nadaj mu taką nazwę: windowLoaded():

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

Po uruchomieniu zdarzenia wczytywania pojawi się ono w zrzucie stosu Narzędzi deweloperskich z nazwą funkcji, a nie z zagadką „(funkcja anonimowa)”. Dzięki temu możesz szybko sprawdzić, co dzieje się w zrzucie stosu.

Przed
Funkcja anonimowa.
Po
Funkcja nazwana

Odkryj więcej

Podsumowując, to są wszystkie asynchroniczne wywołania zwrotne, w których Narzędzia deweloperskie wyświetlą pełny stos wywołań:

  • Minutniki: wróć do miejsca, w którym zainicjowano setTimeout() lub setInterval().
  • XHRs: Przejdź z powrotem do miejsca, w którym nazwano xhr.send().
  • Klatki animacji: wróć do miejsca, w którym wywołano funkcję requestAnimationFrame.
  • Obietnice: wróć do miejsca, w którym obietnica została zrealizowana.
  • Object.observe: wróć do miejsca, w którym było pierwotnie powiązane wywołanie zwrotne obserwatora.
  • MutationObservers: wróć do miejsca, w którym zostało wywołane zdarzenie obserwatora mutacji.
  • window.postMessage(): przejdź do wywołań obsługi wiadomości w trakcie procesu.
  • DataTransferItem.getAsString()
  • Interfejs FileSystem API
  • IndexedDB
  • WebSQL
  • Odpowiednie zdarzenia DOM za pomocą addEventListener(): wróć do miejsca, w którym zostało wywołane zdarzenie. Ze względu na wydajność nie wszystkie zdarzenia DOM kwalifikują się do korzystania z funkcji asynchronicznych stosów wywołań. Przykłady aktualnie dostępnych zdarzeń: „scroll”, „hashchange” i „selectionchange”.
  • Zdarzenia multimedialne w usłudze addEventListener(): wróć do miejsca, w którym zostało wywołane zdarzenie. Dostępne zdarzenia multimedialne obejmują: zdarzenia audio i wideo (np. „play”, „pause”, „ratechange”), zdarzenia z WebRTC MediaStreamTrackList (np. „addtrack”, „removetrack”) oraz zdarzenia MediaSource (np. „sourceopen”).

Pełny zrzut stosu wywołań zwrotnych JavaScript nie powinien mieć wpływu na zaległości. Ta funkcja w Narzędziach deweloperskich jest szczególnie przydatna, gdy względem siebie zachodzi wiele zdarzeń asynchronicznych lub gdy z poziomu asynchronicznego wywołania zwrotnego zostaje powodowany niewykryty wyjątek.

Wypróbuj ją w Chrome. Jeśli chcesz podzielić się opinią na temat tej nowej funkcji, napisz do nas na stronie narzędzia do śledzenia błędów w Narzędziach deweloperskich w Chrome lub w grupie Narzędzi deweloperskich w Chrome.