API de User Timing

Información sobre tu app web

Alex Danilo

Las aplicaciones web de alto rendimiento son fundamentales para ofrecer 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 del tiempo de las áreas del código de JavaScript es la forma ideal de identificar los puntos calientes, que es el primer paso para encontrar 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 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 de punto flotante, el valor puede representar milisegundos fraccionarios y, por lo tanto, puede generar una precisión de milésimas 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 tardaste en 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 son bastante autoexplicativos, 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');

Si configuramos marcas con nombre en toda nuestra aplicación web, podemos recopilar una gran cantidad de datos de tiempo y analizarlos cuando queramos para averiguar qué hace la aplicación y cuándo.

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(), se almacena el resultado independientemente de las marcas que establezcas, de modo que puedas recuperarlo más adelante. Cuando se almacenan los tiempos a medida que se ejecuta la aplicación, esta sigue siendo responsiva y puedes volcar todos los datos después de que la aplicación haya terminado una tarea 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. Para ello, llama 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 te recomendamos que te deshagas de las medidas que hayas realizado. Para ello, existe un método correspondiente 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');

quitará la medida que hicimos 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 algún análisis. Esto también es muy sencillo, solo debes usar la interfaz PerformanceTimeline.

Por ejemplo, el método getEntriesByType() nos permite obtener todos nuestros tiempos de marca o todos nuestros tiempos de medición como una lista para que podamos iterar sobre ellos y procesar los datos. Lo bueno es que la lista se muestra en orden cronológico, por lo 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 muestra 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, el código:

var items = window.performance.getEntriesByName('mark_fully_loaded');

nos mostraría una lista con un elemento que contiene la marca de tiempo "mark_fully_loaded" en la propiedad startTime.

Programación de una solicitud XHR (ejemplo)

Ahora que tenemos una idea clara de la API de User Timing, podemos usarla para analizar cuánto tiempo tardan 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 tanto, normalmente, nuestro XMLHttpRequest se vería de la siguiente manera:

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 hacer un seguimiento de la cantidad de solicitudes y también para usarlo y almacenar una medida para cada solicitud que se realice. 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, podríamos 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 brinda muchas herramientas excelentes para aplicar a cualquier aspecto de tu aplicación web. Para reducir los puntos críticos de tu aplicación, puedes usar llamadas a la API en toda tu aplicación web y procesar posteriormente los datos de tiempo generados para crear una imagen clara de dónde se está invirtiendo el tiempo. Pero ¿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 la API de User Timing en tus aplicaciones ahora. Descubrirás cómo acelerarlas y tus usuarios te agradecerán por mejorar tanto su experiencia.