Stroj času k ladění asynchroního JavaScriptu v Chrome DevTools

Vývojářské nástroje v Google Chrome přináší zajímavou novinku. Jedná se o stroj času, pomocí kterého můžete snadno ladit ajaxová volání, resp. procházet různými stavy vaší aplikace v čase pomocí zásobníku asynchronních volání.

Tento článek je překladem textu Debugging Asynchronous JavaScript with Chrome DevTools od Pearl Chen a je zveřejněn pod licencí CC BY 3.0.

Úvod

Asynchronní zpracování pomocí callback funkcí je mocnou stránkou JavaScriptu, ale současně se dost špatně ladí.

Naštěstí Chrome Canary DevTools přináší řešení, můžete v něm vidět celý zásobník javascriptových asynchronních volání!

A quick teaser overview of async call stacks
Na začátek takový rychlý náhled, jak to celé funguje.

 

Jakmile povolíte zásobník asynchronních volání v DevTools, můžete se nořit do různých stavů vaší webové aplikace v čase. Můžete procházet celý zásobník handlerů událostí, volání setInterval, setTimeout, XMLHttpRequest, promises, requestAnimationFrame, MutationObservers a další.

Jak budete zásobníkem volání procházet, můžete současně analyzovat hodnoty jakékoliv proměnné v daném bodě spuštění vaší aplikace. Funguje to podobně jako stroj času.

Pojďme se podívat na několik scénářů použití.

Jak povolit asynchronní ladění v Chrome Canary

Použijte Chrome Canary (build 35 nebo vyšší). Zobrazte si panel Sources v DevTools.

Hned vedle panelu Call Stack napravo najdete checkbox „Async“. Ten asynchronní ladění zapíná a vypíná.

Toggle the async feature on or off

Zachytávání událostí časovače a XHR odpovědí

Nejspíš jste něco podobného v Gmailu už viděli:

Gmail retrying to send an email

Pokud nastane problém při odesílání požadavku (ať už je problém na serveru nebo v síťové konektivitě klienta), Gmail se po krátké odmlce automaticky pokusí zprávu poslat znovu.

Abychom si to mohli snadno vyzkoušet, vytvořila jsem mockup Gmailu. Funguje dle schématu:

Flow chart of mock Gmail example
Metody modře zvýrazněné fungují asynchronně.

 

Pokud se podíváte na obyčejný zásobník volání, tak breakpoint na postOnFail() vám nic moc nenapoví, odkud byla funkce vyvolána. Zkusme zapnout asynchronní zásobník a hned je jasno:

Předtím:

Breakpoint set in mock Gmail example without async call stacks
Klasický zásobník, vidíme, že postOnFail() je ajaxový callback, ale nic víc.

Potom:

Breakpoint set in mock Gmail example with async call stacks
Asynchronní zásobník volání, vidíme, že XHR vyvolal submitHandler(), který byl vyvolán obsluhou události click ze scripts.js. Pěkné!

 

V celém zásobníku pak uvidíme, že požadavek pochází buď z submitHandler() jako na obrázku výše anebo z  retrySubmit() jako na obrázku níže:

Another breakpoint set in mock Gmail example with async call stacks

Ze zásobníku také poznáme, zda byl požadavek vyvolán obsluhou události, např. kliknutím nebo metodou setTimeout() nebo některou z dalších asynchronních metod.

Sledujte výrazy asynchronně

Při procházení asynchronním zásobníkem můžete sledovat vybrané výrazy (watch expression), které budou zobrazovat hodnotu odpovídající patřičnému stavu aplikace v čase!

An example of using watch expressions with aysnc call stacks

Změňte minulost aneb jak vykonat kód v minulých rozsazích působnosti (scopes)

Minulost můžete nejen zkoumat, ale také v ní spouštět váš kód pomocí javascriptové console.

Představte si, že jste Doktorem Who a potřebujete porovnat čas před vstupem do TARDIS a „nyní“. Žádný problém.

An example of using the JavaScript console with aysnc call stacks
Použití javascriptové konzole ve spojení s asynchronním zásobníkem. Demo použité v obrázku najdete zde.

 

Ušetří vám to čas. Můžete zůstat a pracovat v DevTools, místo abyste přepínali do vašich zdrojáků, upravovali je, znovu načítali atd.

Brzy uvidíte: použití se zřetězenými promises

A co teprve, pokud používáte promises. Převezmu finální příklad z tutoriálu Jake Archibalda k javascriptovým promises.

Flow diagram s javascriptovými promises.

 

Předtím:

Breakpoint set in promises example without async call stacks
Klasický zásobník, všimněte i, že v panelu Call Stack toho opravdu moc není.

 

Potom:

Breakpoint set in promises example with async call stacks
A nyní? Skvělé! Náš zásobník je plný.

 

Podpora pro promises se objeví brzy (doufejme, že v Chrome 36). Pokud si ji chcete vyzkoušet už dnes, můžete si ji v Chrome 33 nebo Chrome 34 zapnout na stránce chrome://flags/#enable-devtools-experiments povolením Developer Tools experimentů. Po restartu prohlížeče najdete v nastavení DevTools volbu enable support for async stack traces.

Projděme se animací

Můžeme jít ještě dál. Paul Lewis napsal článek Leaner, Meaner, Faster Animations with requestAnimationFrame. Otevřeme si jeho requestAnimationFrame demo a nastavíme breakpoint na začátek metody update() method (okolo řádku 874) v post.html.

Předtím:

Breakpoint set in requestAnimationFrame example without async call stacks
Panel Call Stack toho moc neobsahuje.

 

Potom:

Breakpoint set in requestAnimationFrame example with async call stacks
A nyní?

 

Sledování změn DOMu pomocí MutationObserverů

MutationObserver nám umožňuje sledovat změny v DOMu. V tomto jednoduchém příkladu je po kliknutí na tlačítko přidán na konec dokumentu <div class="rows"></div>.

 

Přidáme breakpoint do metody nodeAdded() (řádka 31) v demo.html a podíváme se na výsledek.

Předtím:

Breakpoint set in mutationObserver example without async call stacks
Obyčejný zásobník

 

Potom:

Breakpoint set in mutationObserver example with async call stacks
Asynchronní zásobník

 

Tipy k ladění asynchronního JavaScriptu

Pojmenujte vaše funkce

Pokud používáte ve vašich callbacích anonymní funkce, můžete je místo toho chtít pojmenovat, což procházení asynchonním zásobníkem dost zjednoduší.

Místo anonymní funkce:

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

Zkuste:

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

A výsledek?

S anonymní funkcí:

An anonymous function

S pojmenovanou funkcí:

A named function

Shrnutí

Přehled asynchronních callbacků, které můžete sledovat v asynchronním zásobníku:

  • Timers: metody setTimeout() a setInterval()
  • XHRs: zavolání xhr.send()
  • Animation frames: volání requestAnimationFrame.
  • Event listeners: vysledování až k addEventListener().
  • MutationObservers: sledování, jaká událost je vyvolala.

A brzy přibude podpora pro tato experimentální javascriptová API:

  • Promises
  • Object.observe

Martin Hassman založil a vede magazín Zdroják. Absolvoval VŠCHT Praha. Byl u založení projektu CZilla (dnes už nepamatujete, nevadí). Stavěl mosty a metal cestu pro HTML5 (to tu ještě máme). V GUG.cz organizoval akce pro vývojáře (a jestli neumřeli, kódují si dodnes…).

Komentáře: 13

Přehled komentářů

steinbauer Dvě chybky
tarmaq Re: Dvě chybky
steinbauer Re: Dvě chybky
Martin Hassman Re: Dvě chybky
Jirka Kosek Re: Dvě chybky
Martin Hassman Re: Dvě chybky
Jirka Kosek Re: Dvě chybky
Martin Hassman Re: Dvě chybky
vaclav.sir Re: Dvě chybky
Martin Hassman Re: Dvě chybky
fish nefunguje
Martin Hassman Re: nefunguje
skiper.skiprovic q
Zdroj: https://www.zdrojak.cz/?p=11814