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

Wprowadzenie

Potężną funkcją, która wyróżnia JavaScript, jest możliwość działania asynchronicznego za pomocą funkcji wywołania zwrotnego. Przypisanie wywołań asynchronicznych pozwala pisać kod oparty na zdarzeniach, ale powoduje też, że śledzenie błędów staje się męczące, ponieważ kod JavaScript nie jest wykonywany w sposób liniowy.

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

Krótki wstęp do tematu wywołań asynchronicznych.
Szybkie omówienie stosów wywołań asynchronicznych. (wkrótce omówimy przebieg tego demo).

Po włączeniu funkcji wywołania asynchronicznego w DevTools możesz analizować stan aplikacji internetowej w różnych momentach. Przejrzyj pełny ślad stosu w przypadku niektórych metod obsługi zdarzeń, funkcji setInterval, setTimeout, XMLHttpRequest, requestAnimationFrame, MutationObservers i innych.

Podczas analizowania ścieżki wywołań możesz też sprawdzić wartość dowolnej zmiennej w danym punkcie wykonywania kodu. To jak wehikuł czasu dla Twoich zegarków.

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

Włączanie debugowania asynchronicznego w Chrome

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

Po prawej stronie obok panelu Stos wywołań znajduje się nowe pole wyboru „Asynchronicznie”. Zaznacz lub odznacz pole wyboru, aby włączyć lub wyłączyć debugowanie asynchroniczne. (Chociaż po włączeniu tej opcji możesz nie chcieć jej wyłączać).

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

rejestrowanie opóźnionych zdarzeń licznika i odpowiedzi XHR;

Prawdopodobnie widzisz to w Gmailu:

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

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

Aby pokazać, jak stosy wywołań asynchronicznych mogą pomóc w analizowaniu opóźnionych zdarzeń związanych z zegarem i odpowiedzi XHR, odtworzyłem ten przepływ za pomocą przykładowego Gmaila. Pełny kod JavaScript znajdziesz, klikając powyższy link, ale proces wygląda tak:

Schemat przepływu danych w przykładowym Gmailu
Na powyższym schemacie metody wyróżnione na niebiesko to najlepsze miejsca na użycie nowej funkcji DevTool, ponieważ działają one asynchronicznie.

W poprzednich wersjach DevTools tylko spojrzenie na panel Zrzut stosu nie dawało wielu informacji o tym, skąd wywoływana jest funkcja postOnFail().postOnFail() Zwróć uwagę na różnicę po włączeniu asynkronicznych stert:

Przed
Punkt przerwania ustawiony w przykładowym kodzie Gmaila bez asynkronicznych stosów wywołań.
Panel wywołań bez włączonego asynchronicznego wywołania.

Tutaj widać, że postOnFail() zostało wywołane z poziomu wywołania zwrotnego AJAX, ale nie ma dalszych informacji.

Po
Punkt przerwania ustawiony w przykładowym Gmailu z asynchronicznymi stosami wywołań.
Panel Stos wywołań z włączoną funkcją asynchroniczności.

Tutaj widać, że żądanie XHR zostało zainicjowane z urządzenia submitHandler(). Super!

Po włączeniu nieblokujących stertowników wywołań możesz wyświetlić cały sterownik wywołań, aby łatwo sprawdzić, czy żądanie zostało zainicjowane z poziomu submitHandler() (co następuje po kliknięciu przycisku przesłania) czy z poziomu retrySubmit() (co następuje po opóźnieniu setTimeout()):

submitHandler()
Punkt przerwania ustawiony w przykładowym kodzie Gmaila z asynchronicznymi stosami wywołań
retrySubmit()
Kolejny punkt przerwania ustawiony w przykładowym kodzie Gmaila z asynchronicznymi stosami wywołań

Obserwowane wyrażenia niesynchroniczne

Gdy przejdziesz przez cały stos wywołań, obserwowane wyrażenia zostaną zaktualizowane, aby odzwierciedlały stan wyrażenia w danym momencie.

Przykład użycia wyrażeń watch w przypadku wywołań asynchronicznych

Ocenianie kodu z poprzednich zakresów

Oprócz obserwowania wyrażeń możesz wchodzić w interakcję z kodem z poprzednich zakresów w panelu konsoli JavaScript w Narzędziach deweloperskich.

Wyobraź sobie, że jesteś Doktorem Who i potrzebujesz pomocy przy porównaniu zegara z czasu, gdy wsiadłeś/wsiałaś do TARDIS, z czasem „obecnym”. W konsoli DevTools możesz łatwo oceniać, przechowywać i wykonywać obliczenia dotyczące wartości z różnych punktów wykonania.

Przykład korzystania z konsoli JavaScript z asynchronicznymi stosami wywołań
Aby debugować kod, użyj konsoli JavaScript w połączeniu ze stosami wywołań asynchronicznych. Powyższe demo znajdziesz tutaj.

Pozostawanie w Narzędziach deweloperskich podczas manipulowania wyrażeniami pozwoli Ci zaoszczędzić czas, który musiałbyś poświęcić na przełączenie się z powrotem do kodu źródłowego, wprowadzenie zmian i odświeżenie przeglądarki.

Rozwiązywanie łańcuchów obietnic

Jeśli poprzednie symulowane działanie Gmaila było trudne do zrozumienia bez włączonej funkcji wywołań asynchronicznych, wyobraź sobie, jak trudne byłoby to w przypadku bardziej złożonych procesów asynchronicznych, takich jak łańcuchowe obietnice. Przyjrzyjmy się jeszcze raz ostatniemu przykładowi z samouczka Jake'a Archibalda o obietnicach JavaScriptu.

Oto krótka animacja pokazująca przeglądanie sterty wywołań w przykładzie async-best-example.html Jake'a.

Przed
Punkt przerwania ustawiony w obietnicy bez asynkronicznych stosów wywołań
Panel wywołań bez włączonego asynchronicznego wywołania.

Podczas debugowania obietnic panel wywołań zawiera niewiele informacji.

Po
Punkt przerwania ustawiony w obietnicy z asynchronicznymi stosami wywołań
Panel Stos wywołań z włączoną funkcją asynchroniczności.

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

Wgląd w statystyki dotyczące animacji internetowych

Przyjrzyjmy się bliżej archiwom HTML5Rocks. Pamiętasz artykuł Leaner, Meaner, Faster Animations with requestAnimationFrame Paula Lewisa?

Otwórz demo requestAnimationFrame i dodaj punkt przerwania na początku metody update() (około wiersza 874) w pliku post.html. Dzięki grupom wywołań asynchronicznych uzyskujemy znacznie więcej informacji o requestAnimationFrame, w tym możliwość przejścia aż do wywołania zwrotnego zdarzenia przewijania, które rozpoczęło działanie.

Przed
Punkt przerwania ustawiony w przykładzie metody requestAnimationFrame bez stosów wywołań asynchronicznych
Panel wywołań bez włączonego asynchronicznego wywołania.
Po
Punkt przerwania ustawiony w przykładzie z metodą requestAnimationFrame z asynchronicznymi stosami wywołań
włączeniu asynchroniczności.

Śledzenie zmian w DOM przy użyciu MutationObserver

MutationObserver pozwalają nam obserwować zmiany w DOM. W tym prostym przykładzie po kliknięciu przycisku do elementu <div class="rows"></div> zostanie dołączony nowy węzeł DOM.

Dodaj punkt przerwania w pliku demo.html (wiersz 31) w bloku nodeAdded(). Dzięki włączonym asynkronizowanym grupom wywołań możesz teraz przejść przez grupę wywołań od addNode() do początkowego zdarzenia kliknięcia.

Przed
Punkt przerwania ustawiony w przykładzie mutationObserver bez wywołań asynchronicznych.
Panel wywołań bez włączonego asynchronicznego wywołania.
Po
Punkt przerwania ustawiony w przykładie mutationObserver z asynchronicznymi stosami wywołań.
włączeniu asynchroniczności.

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

Nazywanie funkcji

Jeśli wszystkie funkcje wywołania przypisujesz jako funkcje anonimowe, możesz nadać im nazwy, aby ułatwić wyświetlanie stosu wywołań.

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

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

Nadaj mu nazwę, np. windowLoaded():

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

Gdy zdarzenie load zostanie wywołane, pojawi się w wyświetleniu ścieżki wywołań w DevTools z nazwą funkcji zamiast enigmatycznego komunikatu „(anonymous function)”. Dzięki temu łatwiej jest na pierwszy rzut oka zobaczyć, co dzieje się w wyświetleniu ścieżki sterowania.

Przed
Funkcja anonimowa.
Po
Funkcja nazwana

Odkryj więcej

Podsumujmy, które asynchroniczne wywołania zwrotne będą wyświetlać pełny stos wywołań w DevTools:

  • Samowyzwalacze: znajdź miejsce, w którym inicjowana jest funkcja setTimeout() lub setInterval().
  • XHR: znajdź miejsce, w którym wywołano funkcję xhr.send().
  • Klatki animacji: wróć do miejsca, w którym wywołano funkcję requestAnimationFrame.
  • Obietnice: wróć do miejsca, w którym obietnica została spełniona.
  • Object.observe: znajdź miejsce, w którym funkcja wywołania zwrotnego obserwatora została pierwotnie powiązana.
  • MutationObservers znajdź miejsce, w którym zostało wywołane zdarzenie obserwatora mutacji.
  • window.postMessage(): przeglądaj wywołania wiadomości w ramach procesu.
  • DataTransferItem.getAsString()
  • FileSystem API
  • IndexedDB
  • WebSQL
  • Zdarzenia DOM kwalifikujące się do addEventListener(): wróć do miejsca, w którym zostało wywołane zdarzenie. Ze względów związanych z wydajnością nie wszystkie zdarzenia DOM kwalifikują się do korzystania z funkcji wywołań asynchronicznych. Przykłady obecnie dostępnych zdarzeń: „scroll”, „hashchange” i „selectionchange”.
  • Zdarzenia multimedialne za pomocą addEventListener(): wróć do miejsca, w którym zostało wywołane zdarzenie. Dostępne zdarzenia multimedialne to: zdarzenia audio i wideo (np. „play”, „pause”, „ratechange”), zdarzenia WebRTC MediaStreamTrackList (np. „addtrack”, „removetrack”) i zdarzenia MediaSource (np. „sourceopen”).

Możliwość wyświetlenia pełnego śledzenia stosu wywołań kodu JavaScript powinna pomóc Ci zachować nerwy. Ta funkcja w DevTools będzie szczególnie przydatna, gdy wiele zdarzeń asynchronicznych występuje w zależności od siebie lub gdy z wewnętrznego wywołania asynchronicznego zostanie wyrzucone nieprzechwycone wyjątek.

Wypróbuj to w Chrome. Jeśli chcesz podzielić się opinią na temat tej nowej funkcji, skontaktuj się z nami za pomocą śledzika błędów w Narzędziach deweloperskich w Chrome lub w grupie Narzędzi deweloperskich w Chrome.