Introdução
Um recurso eficiente que torna o JavaScript único é a capacidade de funcionar de maneira assíncrona, usando funções de callback. A atribuição de callbacks assíncronos permite que você escreva um código orientado por eventos, mas também transforma o rastreamento de bugs em uma experiência difícil, já que o JavaScript não está sendo executado de maneira linear.
Felizmente, agora no Chrome DevTools, você pode conferir a pilha de chamadas completa de callbacks JavaScript assíncronos.
Depois de ativar o recurso de pilha de chamadas assíncrona no DevTools, você poderá
analisar o estado do seu app da Web em vários momentos. Acompanhe o stack
trace completo para alguns listeners de eventos, setInterval
, setTimeout
, XMLHttpRequest
,
promessas, requestAnimationFrame
, MutationObservers
e muito mais.
À medida que avança no stack trace, também é possível analisar o valor de qualquer variável nesse ponto específico de execução do ambiente de execução. É como uma máquina do tempo para suas expressões de relógio.
Vamos ativar esse recurso e analisar alguns cenários.
Ativar a depuração assíncrona no Chrome
Ative o novo recurso no Chrome para testar. Acesse o painel Sources do Chrome Canary DevTools.
Ao lado do painel Call Stack à direita, há uma nova caixa de seleção para "Async". Marque a caixa de seleção para ativar ou desativar a depuração assíncrona. Embora depois de ativada, talvez você não queira desativá-la.
Capturar eventos de timer atrasados e respostas XHR
Provavelmente você já viu isto no Gmail:
Se houver um problema ao enviar a solicitação, seja no servidor ou na conectividade de rede do lado do cliente, o Gmail tentará automaticamente reenviar a mensagem após um curto tempo limite.
Para acessar como as pilhas de chamadas assíncronas podem nos ajudar a analisar eventos de timer atrasado e respostas XHR, recriei esse fluxo com um exemplo fictício do Gmail. O código JavaScript completo pode ser encontrado no link acima, mas o fluxo é este:
Analisando apenas o painel "Call Stack" nas versões anteriores do DevTools, um
ponto de interrupção em postOnFail()
forneceria pouca informação sobre de onde
postOnFail()
estava sendo chamado. Mas observe a diferença ao ativar
pilhas assíncronas:
Com as pilhas de chamadas assíncronas ativadas, você pode conferir toda a pilha de chamadas para conferir facilmente
se a solicitação foi iniciada por submitHandler()
(o que acontece depois de clicar no botão "Enviar") ou
retrySubmit()
(que acontece após um atraso de setTimeout()
):
Observar expressões de maneira assíncrona
Quando você percorre a pilha de chamadas completa, as expressões monitoradas também são atualizadas para refletir o estado em que se encontravam naquele momento.
Avaliar o código de escopos anteriores
Além de simplesmente observar as expressões, é possível interagir com seu código de escopos anteriores diretamente no painel do Console JavaScript do DevTools.
Imagine que você é o Dr. Quem e precisa de uma ajudinha para comparar o relógio antes de entrar em 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 suas expressões vai evitar que você precise voltar ao código-fonte, fazer edições e atualizar o navegador.
Resolver resoluções de promessa encadeadas
Se você achava que o fluxo simulado anterior do Gmail era difícil de ser resolvido sem o recurso de pilha de chamadas assíncrona ativado, imagina como seria difícil lidar com fluxos assíncronos mais complexos, como promessas encadeadas? Vamos rever o exemplo final do tutorial de Jake Archibald sobre Promessas em JavaScript.
Veja uma pequena animação de apresentação das pilhas de chamadas no exemplo async-best-example.html de Jake.
Receber insights sobre suas animações da Web
Vamos nos aprofundar nos arquivos do HTML5Rocks. Lembra-se do artigo Leaner, Meaner, Faster Animations with requestAnimationFrame de Paul Lewis?
Abra a demonstração do requestAnimationFrame e adicione um ponto de interrupção no início do método update() (perto da linha 874) de post.html. Com as pilhas de chamadas assíncronas, recebemos muito mais insights sobre o requestAnimationFrame, incluindo a capacidade de voltar até o callback do evento de rolagem inicial.
Rastrear atualizações do DOM ao usar o MutationObserver
Com MutationObserver
, é possível observar as mudanças no DOM. Neste exemplo simples, quando você clica no botão, um novo nó DOM é anexado ao <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, é possível levar a pilha de chamadas de volta pelo addNode()
até o
evento de clique inicial.
Dicas para depurar JavaScript em pilhas de chamadas assíncronas
Nomeie suas funções
Se você costuma atribuir todos os callbacks como funções anônimas, dê 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 a ela, como windowLoaded()
:
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
Quando o evento de carregamento é disparado, ele aparece no stack trace do DevTools com o nome da função, em vez do código misterioso "(função anônima)". Isso facilita muito mais para você ver rapidamente o que está acontecendo no stack trace.
Mais informações
Recapitulando, esses são todos os callbacks assíncronos em que o DevTools exibirá toda a pilha de chamadas:
- 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: volta para onde uma promessa foi resolvida.
- Object.observe: volte para onde o callback do observador foi originalmente vinculado.
- MutationObservers: volta para onde o evento do observador de mutação foi disparado.
- window.postMessage(): percorre as chamadas de mensagens durante o processo.
- DataTransferItem.getAsString()
- API FileSystem
- IndexedDB
- WebSQL
- Eventos DOM qualificados via
addEventListener()
: volte para onde o evento foi disparado. Por motivos de desempenho, nem todos os eventos DOM estão qualificados para o recurso de pilhas de chamadas assíncronas. Exemplos de eventos disponíveis no momento incluem: "scroll", "hashchange" e "selectionchange" - Eventos multimídia via
addEventListener()
: volte para onde o evento foi disparado. Os eventos multimídia disponíveis incluem: eventos de áudio e vídeo (por exemplo, "play", "pause" e "ratechange"), eventos de WebRTC MediaStreamTrackList (por exemplo, "addtrack", "removetrack") e de MediaSource (por exemplo, "sourceopen").
A possibilidade de ver o stack trace completo dos callbacks de JavaScript é uma forma de ter você com isso. Esse recurso no DevTools será especialmente útil quando vários eventos assíncronos acontecerem em relação um ao outro ou se uma exceção não capturada for gerada em um callback assíncrono.
Faça um teste no Chrome. Se você tiver feedback sobre esse novo recurso, conte para a gente no bug Tracker do Chrome DevTools ou no Grupo do Chrome DevTools.