Chrome DevTools part 3 – Breakpoints

Po prawie miesiącu przerwy wracam z kolejną częścią serii o Chrome DevTools. Tym razem zajmę się sprawą debugowania JavaScriptu. Dla niektórych to może być zaskoczeniem, ale debugowanie aplikacji klasyczną metodą alert(‚dupa’) już dawno przestało być szczytem możliwości 🙂

Chrome pozwala na zatrzymywanie i analizę wykonywania skryptów za pomocą breakpointów. Okazuje się, że breakpointy można ustawiać na kilka różnych sposobów. Dzisiejszy post jest przeglądem tych metod.

Na potrzebę tej części przygotowałem prostą aplikację, która losuje sto liczb i wypisuje je na stronie.

<html>
<head>
<meta charset="utf-8">
<title>Breakpoints demo</title>
<script src="http://code.jquery.com/jquery-latest.js"
type="text/javascript"></script>
</head>
<body>
<div id="numbers" />
<script>
(function() {
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max min + 1)) + min;
}
for(var i = 0;i < 100;i++) {
var x = getRandomInt(0, 100);
$('#numbers').append(x).append("<br/>");
if (x === 0) {
throw "ZERO!";
}
}
})();
</script>
</body>
</html>

view raw
breakpoints.html
hosted with ❤ by GitHub

1. Line of code

Najprostsza metoda debugowania – zatrzymuje program zawsze podczas wykonania danej linii. Taki breakpoint można ustawić klikając lewym klawiszem myszy na numer linii.

Ten sam efekt można osiągnąć wpisując debugger w kodzie aplikacji.

Zrzut ekranu 2017-07-04 o 19.48.49

 

 

 

2. Breakpoint warunkowy

Załóżmy, że interesuje nas stan aplikacji jedynie w ostatnim obiegu pętli for. Nie musimy dodawać do naszego kodu dodatkowego warunku rodzaju:

if (i == 99) { debugger; }

Zamiast tego można użyć breakpointu warunkowego. W tym celu należy kliknąć prawym przyciskiem myszy na wybraną linie i podać warunek zatrzymania.

cond
Breakpointy warunkowe

3. DOM breakpoints

Kolejnym sposobem na debugowanie aplikacji są DOM breakpoints. Mogą być one przydatne, gdy np. wiele miejsc w kodzie modyfikuje element drzewa strony. Wtedy zamiast stawiać pojedyncze breakpointy w każdym z tych miejsc możemy dodać DOM breakpoint. 

Zmodyfikujmy nieco kod naszego dema – zamiast pętli użyjemy dwóch timerów dodających liczby do elementu #numbers.

setInterval(function() {
var x = getRandomInt(0, 100);
$('#numbers').append(x).append("<br/>");
}, 1000);
setInterval(function() {
var x = getRandomInt(101, 200);
$('#numbers').prepend("<br/>").prepend(x);
}, 2000);

Załóżmy, że interesuje nas, który z timerów dodał konkretną liczbę. Ustawmy w tym celu DOM breakpoint zatrzymujący program, gdy zawartość #numbers ulegnie zmianie (subtree modifications).

dom.gif
DOM breakpoint

Debugger zatrzyma teraz program, gdy jeden z dwóch timerów spróbuje dodać element do #numbers.

Oprócz subtree modifications mamy również do dyspozycji attribute modifications (zatrzymuje program, gdy atrybut elementu ulegnie zmianie) oraz node removal (gdy element zostanie usunięty).

4. XHR breakpoints

Aplikacje JavaScriptowe bardzo często wykonują setki zapytań do zewnętrznych serwisów. Czasem może się to wymknąć spod kontroli i może zdarzyć się, że nasza aplikacja wywołuje nadmierną liczbę zapytań lub pyta zły adres.

Aby zbadać, które miejsce w naszym kodzie wywołuje dane zapytanie możemy zdefiniować XHR breakpoint, który zatrzyma program w momencie, gdy wywołane zostanie zapytanie, którego adres URL zawiera wybraną przez nas frazę.

Zmodyfikujmy nasz skrypt tak, by jeden z timerów pobierał losową liczbę z serwisu random.org za pomocą funkcji fetch (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch):

setInterval(function() {
var x = getRandomInt(0, 100);
$('#numbers').append(x).append("<br/>");
}, 1000);
setInterval(function() {
fetch('https://www.random.org/integers/?num=1&min=0&max=100&col=1&base=10&format=plain&rnd=new&#39;).then(function(response){
return response.text();
}).then(function(number) {
$('#numbers').prepend("<br/>").prepend(number);
});
}, 2000);
})();

view raw
xhr_breakpoints.js
hosted with ❤ by GitHub

Dodajmy teraz XHR breakpoint, który zatrzyma program, gdy wykonane zostanie zapytanie do random.org:

xhr.gif
XHR breakpoints

5. Event listener breakpoints

Chrome pozwala na dodawanie breakpointów, które reagują na javascriptowe eventy takie, jak kliknięcie, czy wpisywanie tekstu. Więcej o Event Listenerach można przeczytać poprzedniej części serii.

6. Exception breakpoints

Fajną, choć momentami upierdliwą funkcją są breakpointy wyzwalane w momencie wystąpienia wyjątku. Zmieńmy nasz program tak, by losował tylko 0 lub 1. Gdy wylosowane zostanie 0 rzucony zostanie nieobsłużony wyjątek. Gdy wylosowane zostanie 1, rzucamy i łapiemy wyjątek.

Kliknięcie ikonki pauzy spowoduje zatrzymanie programu, gdy wylosowane zostanie 0. Dodatkowo, jeśl włączymy checkbox Pause on caught exceptions, to program zatrzymany zostanie również wtedy, gdy wylosowana zostanie jedynka.

exc.gif
Exception breakpoints

7. Function breakpoints

Fajną opcją Chrome’a, której wcześniej nie znałem są function breakpoints. Takie breakpointy zatrzymują program, gdy wywołana zostanie dana funkcja. Według dokumentacji DevToolsów aby dodać taki breakpoint należy w kodzie naszej aplikacji dodać linię:

debug(funkcja)

Zmienna funkcja jest identyfikatorem funkcji, którą chcemy śledzić.
Niestety, mi nie udało się odpalić tego breakpointa (ReferenceError) z poziomu kodu i zadziałał dopiero gdy ustawiłem zwykły breakpoint na początku programu i wpisałem w konsoli chrome’a debug(myFunction). 

function myFunction() {
console.log('my func');
}
setInterval(function() {
var x = getRandomInt(0, 1);
if (x == 0) {
myFunction();
}
$('#numbers').append(x).append("<br/>");
}, 1000);

func
Function breakpoints

Podsumowanie

Mimo, że najczęściej przy debugowaniu używamy najprostszej metody – zwykłego zatrzymania na danej linijce kodu, to uważam, że warto mieć gdzieś z tyłu głowy pozostałe opisane przeze mnie techniki. Być może pozwolą nam zaoszczędzić kiedyś sporo czasu.

W następnej części zobaczymy, jak za pomocą DevTools analizować wydajność naszej aplikacji.