Comprende tu app web
Las aplicaciones web de alto rendimiento son fundamentales para una excelente experiencia del usuario. A medida que las aplicaciones web se vuelven cada vez más complejas, comprender el impacto en el rendimiento es fundamental para crear una experiencia atractiva. En los últimos años, aparecieron varias APIs diferentes en el navegador para ayudar a analizar el rendimiento de la red, los tiempos de carga, etc., pero estas no necesariamente proporcionan detalles detallados con suficiente flexibilidad para encontrar lo que ralentiza tu aplicación. Ingresa a la API de User Timing, que proporciona un mecanismo que puedes usar para instrumentar tu aplicación web y así identificar dónde pasa su tiempo. En este artículo, hablaremos de la API y de ejemplos de cómo usarla.
No puedes optimizar lo que no puedes medir
El primer paso para acelerar una aplicación web lenta es determinar dónde se gasta el tiempo. Medir el impacto temporal de las áreas del código JavaScript es la forma ideal de identificar los hotspots, ya que este es el primer paso para descubrir cómo mejorar el rendimiento. Afortunadamente, la API de User Timing proporciona una forma de insertar llamadas a la API en diferentes partes de tu código JavaScript y, luego, extraer datos de tiempos detallados que se pueden usar para ayudarte a realizar optimizaciones.
Tiempo de alta resolución y now()
La precisión es una parte fundamental de la medición del tiempo. En el pasado, teníamos tiempos basados en mediciones de milisegundos, lo cual está bien, pero compilar un sitio sin interrupciones a 60 fps significa que cada fotograma debe dibujarse en 16 ms. Por lo tanto, cuando solo tienes precisión de milisegundos, carece de la precisión necesaria para un buen análisis. Ingresa High Resolution Time, un nuevo tipo de tiempo integrado en los navegadores modernos. El tiempo de alta resolución nos brinda marcas de tiempo de punto flotante que pueden ser precisas hasta la resolución de microsegundos, mil veces mejor que antes.
Para obtener la hora actual en tu aplicación web, llama al método now()
, que forma una extensión de la interfaz de Rendimiento. En el siguiente código, se muestra cómo hacerlo:
var myTime = window.performance.now();
Hay otra interfaz llamada PerformanceTiming que proporciona una serie de tiempos diferentes relacionados con la carga de tu aplicación web. El método now()
muestra el tiempo transcurrido desde que ocurrió el tiempo navigationStart
en PerformanceTiming.
El tipo DOMHighResTimeStamp
Cuando intentabas cronometrar aplicaciones web en el pasado, usabas algo como Date.now()
, que muestra un DOMTimeStamp. DOMTimeStamp muestra un número entero de milisegundos como su valor. Para proporcionar la precisión más alta necesaria para la hora de alta resolución, se introdujo un nuevo tipo llamado DOMHighResTimeStamp. Este tipo es un valor de punto flotante que también muestra la hora en milisegundos. Sin embargo, como es un punto flotante, el valor puede representar milisegundos fraccionarios, por lo que puede generar una exactitud de una milésima de milisegundo.
Interfaz de User Timing
Ahora que tenemos marcas de tiempo de alta resolución, usemos la interfaz de User Timing para extraer información de tiempo.
La interfaz de User Timing proporciona funciones que nos permiten llamar a métodos en diferentes lugares de nuestra aplicación que pueden proporcionar un rastro de migas de pan al estilo Hansel y Gretel para permitirnos hacer un seguimiento de dónde se gasta el tiempo.
Usa mark()
El método mark()
es la herramienta principal de nuestro kit de herramientas de análisis de tiempos. Lo que hace mark()
es almacenar una marca de tiempo para nosotros. Lo que es muy útil de mark()
es que podemos nombrar la marca de tiempo, y la API recordará el nombre y la marca de tiempo como una sola unidad.
Llamar a mark()
en varios lugares de tu aplicación te permite calcular cuánto tiempo te llevó alcanzar esa "marca" en tu aplicación web.
La especificación indica una serie de nombres sugeridos para marcas que podrían ser interesantes y que se explican por sí mismos, como mark_fully_loaded
, mark_fully_visible
,mark_above_the_fold
, etcétera.
Por ejemplo, podríamos establecer una marca para cuando la aplicación se cargue por completo con el siguiente código:
window.performance.mark('mark_fully_loaded');
Al establecer marcas con nombre en nuestra aplicación web, podemos recopilar una gran cantidad de datos de tiempo y analizarlos cuando nos resulte conveniente para decidir qué hace la aplicación y cuándo lo hace.
Cómo calcular mediciones con measure()
Una vez que hayas establecido varias marcas de tiempo, querrás saber el tiempo transcurrido entre ellas. Para ello, usa el método measure()
.
El método measure()
calcula el tiempo transcurrido entre las marcas y también puede medir el tiempo entre tu marca y cualquiera de los nombres de eventos conocidos en la interfaz PerformanceTiming.
Por ejemplo, puedes calcular el tiempo desde que se completa el DOM hasta que se carga completamente el estado de tu aplicación con un código como el siguiente:
window.performance.measure('measure_load_from_dom', 'domComplete', 'mark_fully_loaded');
Cuando llamas a measure()
, almacena el resultado independientemente de las marcas que establezcas, por lo que puedes recuperarlos más tarde. Si almacenas los tiempos de ausencia mientras se ejecuta tu aplicación, esta sigue respondiendo, y puedes volcar todos los datos después de que haya finalizado algún trabajo para que se puedan analizar más adelante.
Cómo descartar marcas con clearMarks()
A veces, es útil poder deshacerte de muchas marcas que configuraste. Por ejemplo, puedes realizar ejecuciones por lotes en tu aplicación web y, por lo tanto, deseas comenzar de cero en cada ejecución.
Es muy fácil deshacerte de las marcas que hayas configurado llamando a clearMarks()
.
Por lo tanto, el siguiente código de ejemplo borraría todas las marcas existentes que tengas para que puedas volver a configurar una ejecución de tiempo si lo deseas.
window.performance.clearMarks();
Por supuesto, hay algunas situaciones en las que es posible que no quieras borrar todas tus marcas. Por lo tanto, si quieres quitar marcas específicas, solo debes pasar el nombre de la marca que quieres quitar. Por ejemplo, el siguiente código:
window.performance.clearMarks('mark_fully_loaded');
se quita la marca que establecimos en el primer ejemplo y se dejan sin cambios las demás marcas que establecimos.
También puedes deshacerte de cualquier medición que hayas tomado, y hay un método correspondiente para hacerlo llamado clearMeasures()
. Funciona exactamente igual que clearMarks()
, pero en lugar de hacerlo en las mediciones que hayas realizado, Por ejemplo, el código:
window.performance.clearMeasures('measure_load_from_dom');
Quitaremos la medida que tomamos en el ejemplo anterior de measure()
. Si quieres quitar todas las medidas, funciona igual que clearMarks()
, ya que solo debes llamar a clearMeasures()
sin argumentos.
Cómo obtener los datos de tiempo
Es muy útil establecer marcas y medir intervalos, pero en algún momento querrás acceder a esos datos de tiempo para realizar un análisis. Esto es muy simple también; lo único que debes hacer es usar la interfaz PerformanceTimeline
.
Por ejemplo, el método getEntriesByType()
nos permite obtener todos nuestros tiempos de marca, o agotar el tiempo de espera de todas nuestras mediciones en una lista, para que podamos iterar sobre ella y transferir los datos. Lo bueno es que la lista se muestra en orden cronológico, de modo que puedes ver las marcas en el orden en que se marcaron en tu aplicación web.
El siguiente código:
var items = window.performance.getEntriesByType('mark');
nos muestra una lista de todas las marcas que se alcanzaron en nuestra aplicación web, mientras que el código:
var items = window.performance.getEntriesByType('measure');
nos devuelve una lista de todas las medidas que tomamos.
También puedes obtener una lista de entradas con el nombre específico que les asignaste. Por ejemplo, este es el código:
var items = window.performance.getEntriesByName('mark_fully_loaded');
nos devolverá una lista con un elemento que contiene la marca de tiempo “mark_completamente_cargado” en la propiedad startTime
.
Programación de una solicitud XHR (ejemplo)
Ahora que tenemos una buena imagen de la API de User Timing, podemos usarla para analizar cuánto tiempo demoran todas nuestras XMLHttpRequests en nuestra aplicación web.
Primero, modificaremos todas nuestras solicitudes de send()
para emitir una llamada a función que configure las marcas y, al mismo tiempo, cambiaremos nuestras devoluciones de llamada de éxito por una llamada a función que establezca otra marca y, luego, genere una medición del tiempo que tardó la solicitud.
Por lo general, nuestra XMLHttpRequest se vería así:
var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
do_something(e.responseText);
}
myReq.send();
En nuestro ejemplo, agregaremos un contador global para realizar un seguimiento del número de solicitudes y también lo usaremos para almacenar una medida para cada solicitud que se haga. El código para hacer esto se ve de la siguiente manera:
var reqCnt = 0;
var myReq = new XMLHttpRequest();
myReq.open('GET', url, true);
myReq.onload = function(e) {
window.performance.mark('mark_end_xhr');
reqCnt++;
window.performance.measure('measure_xhr_' + reqCnt, 'mark_start_xhr', 'mark_end_xhr');
do_something(e.responseText);
}
window.performance.mark('mark_start_xhr');
myReq.send();
El código anterior genera una medición con un valor de nombre único para cada XMLHttpRequest que enviamos. Suponemos que las solicitudes se ejecutan en secuencia. El código para las solicitudes en paralelo debería ser un poco más complejo para controlar las solicitudes que se muestran fuera de orden. Dejaremos eso como un ejercicio para el lector.
Una vez que la aplicación web haya realizado varias solicitudes, podremos volcarlas todas en la consola con el siguiente código:
var items = window.performance.getEntriesByType('measure');
for (var i = 0; i < items.length; ++i) {
var req = items[i];
console.log('XHR ' + req.name + ' took ' + req.duration + 'ms');
}
Conclusión
La API de User Timing te ofrece muchas herramientas excelentes para aplicar a cualquier aspecto de tu aplicación web. La reducción de los hotspots en tu aplicación se puede lograr fácilmente a través de llamadas a la API en toda tu aplicación web y un procesamiento posterior de los datos de tiempo generados para crear una imagen clara de dónde se está gastando el tiempo. ¿Qué sucede si tu navegador no es compatible con esta API? No hay problema. Puedes encontrar un polyfill excelente aquí que emula la API muy bien y también funciona bien con webpagetest.org. ¿Qué está esperando? Prueba ahora la API de User Timing en tus aplicaciones, descubrirás cómo acelerarlas y tus usuarios te agradecerán por hacer que su experiencia sea mucho mejor.