Introdução
Um recurso poderoso que torna o JavaScript único é a capacidade de trabalhar de forma assíncrona usando funções de callback. A atribuição de callbacks assíncronos permite escrever código orientado a eventos, mas também torna o rastreamento de bugs uma experiência frustrante, já que o JavaScript não é executado de maneira linear.
Felizmente, agora no Chrome DevTools, você pode conferir a pilha de chamadas completa de callbacks assíncronos de JavaScript.
Depois de ativar o recurso de pilha de chamadas assíncronas no DevTools, você poderá
analisar o estado do seu app da Web em vários momentos. Percorrer o
stack trace completo de alguns listeners de eventos, setInterval
, setTimeout
, XMLHttpRequest
,
promessas, requestAnimationFrame
, MutationObservers
e muito mais.
Ao analisar o stack trace, você também pode analisar o valor de qualquer variável nesse ponto específico da execução do ambiente de execução. É como uma máquina do tempo para expressões de relógio!
Vamos ativar esse recurso e conferir alguns desses cenários.
Ativar a depuração assíncrona no Chrome
Teste esse novo recurso ativando-o no Chrome. Acesse o painel Sources do Chrome Canary DevTools.
Ao lado do painel Call Stack no lado direito, há uma nova caixa de seleção para "Async". Marque ou desmarque a caixa de seleção para ativar ou desativar a depuração assíncrona. No entanto, depois de ativada, talvez você não queira desativar essa opção.
Capturar eventos de timer atrasados e respostas XHR
Você provavelmente já viu isso no Gmail:
Se houver um problema ao enviar a solicitação (o servidor está com problemas ou há problemas de conectividade de rede no lado do cliente), o Gmail vai tentar reenviar a mensagem automaticamente após um breve tempo limite.
Para saber como as pilhas de chamadas assíncronas podem nos ajudar a analisar eventos de timer atrasados e respostas XHR, recriei esse fluxo com um exemplo simulado do Gmail. O código JavaScript completo pode ser encontrado no link acima, mas o fluxo é o seguinte:
Ao analisar apenas o painel "Call Stack" em versões anteriores das Ferramentas do desenvolvedor, um
ponto de interrupção em postOnFail()
forneceria poucas informações sobre onde
postOnFail()
estava sendo chamado. Mas observe a diferença ao ativar
pilhas assíncronas:
Com as pilhas de chamadas assíncronas ativadas, é possível conferir toda a pilha de chamadas para saber se a solicitação foi iniciada em submitHandler()
(o que acontece depois de clicar no botão de envio) ou em
retrySubmit()
(o que acontece após um atraso de setTimeout()
):
Observar expressões de forma assíncrona
Quando você percorre a pilha de chamadas completa, suas expressões observadas também são atualizadas para refletir o estado em que estavam no momento.
Avaliar o código de escopos anteriores
Além de simplesmente monitorar expressões, você pode interagir com o código de escopos anteriores no painel do console JavaScript do DevTools.
Imagine que você é o Dr. Who e precisa de uma ajudinha para comparar o relógio de antes de entrar no Tardis com o "agora". No console do DevTools, é possível avaliar, armazenar e fazer cálculos com facilidade em valores de diferentes pontos de execução.
Ficar no DevTools para manipular as expressões economiza tempo, porque você não precisa voltar ao código-fonte, fazer edições e atualizar o navegador.
Desfazer resoluções de promessas encadeadas
Se você achou que o fluxo de Gmail simulado anterior era difícil de desvendar sem o recurso de pilha de chamadas assíncrona ativado, imagine o quanto seria mais difícil com fluxos assíncronos mais complexos, como promessas conectadas. Vamos revisitar o exemplo final do tutorial de Jake Archibald sobre promessas em JavaScript.
Confira uma pequena animação de como percorrer as pilhas de chamadas no exemplo async-best-example.html de Jake.
Receba insights sobre suas animações da Web
Vamos nos aprofundar nos arquivos do HTML5Rocks. Lembra do artigo de Paul Lewis Animações mais simples, mais rápidas e mais eficientes com requestAnimationFrame?
Abra a demonstração de requestAnimationFrame e adicione um ponto de interrupção no início do método update() (por volta da linha 874) de post.html. Com as pilhas de chamadas assíncronas, temos muito mais insights sobre requestAnimationFrame, incluindo a capacidade de voltar até o callback do evento de rolagem inicial.
Rastrear atualizações do DOM ao usar MutationObserver
Os MutationObserver
permitem observar mudanças no DOM. Neste exemplo simples,
quando você clica no botão, um novo nó DOM é anexado a <div class="rows"></div>
.
Adicione um ponto de interrupção em nodeAdded()
(linha 31) em demo.html. Com as pilhas de chamadas
assíncronas ativadas, agora é possível percorrer a pilha de chamadas de volta por addNode()
até o
evento de clique inicial.
Dicas para depurar JavaScript em pilhas de chamadas assíncronas
Nomear as funções
Se você tende a atribuir todos os callbacks como funções anônimas, é recomendável dar um nome a eles para facilitar a visualização da pilha de chamadas.
Por exemplo, considere uma função anônima como esta:
window.addEventListener('load', function() {
// do something
});
E dê um nome como windowLoaded()
:
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
Quando o evento de carregamento é acionado, ele aparece no stack trace do DevTools com o nome da função, em vez do "(função anônima)". Isso facilita a visualização rápida do que está acontecendo no stack trace.
Mais informações
Para recapitular, estes são todos os callbacks assíncronos em que o DevTools vai mostrar a pilha de chamadas completa:
- Timers:
volte para onde
setTimeout()
ousetInterval()
foi inicializado. - XHRs:
volte para onde
xhr.send()
foi chamado. - Frames de animação:
volte para onde
requestAnimationFrame
foi chamado. - Promessas: volte para onde uma promessa foi resolvida.
- Object.observe: volte para onde o callback do observador foi vinculado originalmente.
- MutationObservers: volte para onde o evento do observador de mutação foi acionado.
- window.postMessage(): analise as chamadas de mensagens intraprocessuais.
- DataTransferItem.getAsString()
- API FileSystem
- IndexedDB
- WebSQL
- Eventos DOM qualificados por
addEventListener()
: volte para onde o evento foi acionado. Por motivos de desempenho, nem todos os eventos do DOM estão qualificados para o recurso de pilhas de chamadas assíncronas. Exemplos de eventos disponíveis atualmente incluem: "scroll", "hashchange" e "selectionchange". - Eventos multimídia por
addEventListener()
: volte para onde o evento foi acionado. Os eventos multimídia disponíveis incluem: eventos de áudio e vídeo (por exemplo, "play", "pause", "ratechange"), eventos MediaStreamTrackList do WebRTC (por exemplo, "addtrack", "removetrack") e eventos MediaSource (por exemplo, "sourceopen").
Ter acesso ao stack trace completo dos seus callbacks JavaScript vai manter seus cabelos. Esse recurso nas Ferramentas do desenvolvedor será especialmente útil quando vários eventos assíncronos ocorrerem em relação uns aos outros ou se uma exceção não detectada for gerada em um callback assíncrono.
Teste no Chrome. Se você tiver feedback sobre esse novo recurso, envie uma mensagem para o rastreador de bugs do Chrome DevTools ou para o grupo do Chrome DevTools.