Introducción
Una función potente que hace que JavaScript sea único es su capacidad para trabajar de forma asíncrona a través de funciones de devolución de llamada. Asignar devoluciones de llamada asíncronas te permite escribir código orientado a eventos, pero también hace que el seguimiento de errores sea una experiencia frustrante, ya que JavaScript no se ejecuta de forma lineal.
Por suerte, ahora en las Herramientas para desarrolladores de Chrome, puedes ver la pila de llamadas completa de las devoluciones de llamada asíncronas de JavaScript.
Una vez que habilites la función de pila de llamadas asíncronas en DevTools, podrás analizar el estado de tu app web en varios momentos. Explora el seguimiento de pila completo de algunos objetos de escucha de eventos, setInterval
, setTimeout
, XMLHttpRequest
, promesas, requestAnimationFrame
, MutationObservers
y mucho más.
A medida que recorres el seguimiento de pila, también puedes analizar el valor de cualquier variable en ese punto en particular de la ejecución del tiempo de ejecución. Es como una máquina del tiempo para tus expresiones de reloj.
Habilitamos esta función y veamos algunas de estas situaciones.
Habilita la depuración asíncrona en Chrome
Para probar esta nueva función, habilítala en Chrome. Ve al panel Sources de las herramientas para desarrolladores de Chrome Canary.
Junto al panel Call Stack, en el lado derecho, hay una nueva casilla de verificación para "Async". Activa o desactiva la casilla de verificación para activar o desactivar la depuración asíncrona. (Aunque, una vez que esté activada, es posible que no quieras desactivarla).
Captura eventos de temporizador retrasados y respuestas XHR
Es probable que ya hayas visto esto en Gmail:
Si hay un problema para enviar la solicitud (el servidor tiene problemas o hay problemas de conectividad de red del cliente), Gmail intentará volver a enviar el mensaje automáticamente después de un breve tiempo de espera.
Para ver cómo las pilas de llamadas asíncronas pueden ayudarnos a analizar los eventos de temporizador retrasados y las respuestas de XHR, recreé ese flujo con un ejemplo simulado de Gmail. Puedes encontrar el código JavaScript completo en el vínculo anterior, pero el flujo es el siguiente:
Si solo miras el panel de pila de llamadas en versiones anteriores de DevTools, un punto de interrupción dentro de postOnFail()
te brindaría poca información sobre desde dónde se llamaba a postOnFail()
. Sin embargo, observa la diferencia cuando activas las pilas asíncronas:
Con las pilas de llamadas asíncronas activadas, puedes ver toda la pila de llamadas para ver fácilmente
si la solicitud se inició desde submitHandler()
(lo que sucede después de hacer clic en el botón de envío) o desde
retrySubmit()
(lo que sucede después de una demora de setTimeout()
):
Cómo supervisar expresiones de forma asíncrona
Cuando recorras la pila de llamadas completa, tus expresiones observadas también se actualizarán para reflejar el estado en el que se encontraban en ese momento.
Evalúa el código de alcances anteriores
Además de supervisar las expresiones, puedes interactuar con tu código desde alcances anteriores directamente en el panel de la consola de JavaScript de DevTools.
Imagina que eres el Dr. Who y necesitas un poco de ayuda para comparar el reloj de antes de subirte a la TARDIS con el de “ahora”. Desde la consola de DevTools, puedes evaluar, almacenar y realizar cálculos fácilmente en valores de diferentes puntos de ejecución.
Si te quedas en DevTools para manipular tus expresiones, ahorrarás tiempo, ya que no tendrás que volver a tu código fuente, hacer ediciones ni actualizar el navegador.
Desenreda las resoluciones de promesas encadenadas
Si pensabas que el flujo simulado de Gmail anterior era difícil de desentrañar sin la función de pila de llamadas asíncrona habilitada, ¿puedes imaginar lo mucho más difícil que sería con flujos asíncronos más complejos, como promesas encadenadas? Repasemos el ejemplo final del instructivo de Jake Archibald sobre promesas de JavaScript.
Esta es una pequeña animación de cómo recorrer las pilas de llamadas en el ejemplo async-best-example.html de Jake.
Obtén estadísticas sobre tus animaciones web
Analicemos en más detalle los archivos de HTML5Rocks. ¿Recuerdas Animaciones más ágiles, más rápidas y más eficientes con requestAnimationFrame de Paul Lewis?
Abre la demo de requestAnimationFrame y agrega un punto de interrupción al comienzo del método update() (alrededor de la línea 874) de post.html. Con las pilas de llamadas asíncronas, obtenemos muchas más estadísticas sobre requestAnimationFrame, incluida la capacidad de recorrer todo el camino hasta la devolución de llamada del evento de desplazamiento inicial.
Hacer un seguimiento de las actualizaciones del DOM cuando se usa MutationObserver
MutationObserver
nos permiten observar los cambios en el DOM. En este ejemplo simple, cuando haces clic en el botón, se agrega un nuevo nodo DOM a <div class="rows"></div>
.
Agrega un punto de interrupción dentro de nodeAdded()
(línea 31) en demo.html. Con las pilas de llamadas asíncronas habilitadas, ahora puedes recorrer la pila de llamadas a través de addNode()
hasta el evento de clic inicial.
Sugerencias para depurar JavaScript en pilas de llamadas asincrónicas
Asigna un nombre a tus funciones
Si tiendes a asignar todas tus devoluciones de llamada como funciones anónimas, te recomendamos que les asignes un nombre para facilitar la visualización de la pila de llamadas.
Por ejemplo, toma una función anónima como esta:
window.addEventListener('load', function() {
// do something
});
Y asígnale un nombre como windowLoaded()
:
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
Cuando se active el evento de carga, aparecerá en el seguimiento de pila de DevTools con su nombre de función en lugar de la críptica "(anonymous function)". Esto facilita ver de un vistazo lo que sucede en el seguimiento de pila.
Explora más
En resumen, estas son todas las devoluciones de llamada asíncronas en las que DevTools mostrará la pila de llamadas completa:
- Temporizadores: Regresa al lugar donde se inicializó
setTimeout()
osetInterval()
. - XHR: Regresa al lugar al que se llamó a
xhr.send()
. - Fotogramas de animación: Regresa al lugar al que se llamó a
requestAnimationFrame
. - Promesas: Regresa al punto en el que se resolvió una promesa.
- Object.observe: Regresa al lugar donde se vinculó originalmente la devolución de llamada del observador.
- MutationObservers: Regresa al lugar donde se activó el evento del observador de mutaciones.
- window.postMessage(): Explora las llamadas de mensajería intraproceso.
- DataTransferItem.getAsString()
- API de FileSystem
- IndexedDB
- WebSQL
- Eventos del DOM aptos a través de
addEventListener()
: Regresa al lugar donde se activó el evento. Por motivos de rendimiento, no todos los eventos del DOM son aptos para la función de pilas de llamadas asíncronas. Algunos ejemplos de eventos disponibles actualmente son "scroll", "hashchange" y "selectionchange". - Eventos multimedia a través de
addEventListener()
: Regresa al lugar donde se activó el evento. Los eventos multimedia disponibles incluyen eventos de audio y video (p.ej., "play", "pause", "ratechange"), eventos de WebRTC MediaStreamTrackList (p.ej., "addtrack", "removetrack") y eventos de MediaSource (p.ej., "sourceopen").
Poder ver el seguimiento de pila completo de tus devoluciones de llamada de JavaScript debería mantenerte tranquilo. Esta función de DevTools será especialmente útil cuando ocurran varios eventos asíncronos en relación con otros, o si se genera una excepción no detectada desde una devolución de llamada asíncrona.
Pruébala en Chrome. Si tienes comentarios sobre esta nueva función, escríbenos en el registro de errores de Chrome DevTools o en el grupo de Chrome DevTools.