API de Long Animation Frames

La API de Long Animation Frames (LoAF, que se pronuncia "Lo-Af") es una actualización de la API de Long Tasks para brindar una mejor comprensión de las actualizaciones lentas de la interfaz de usuario (IU). Esto puede ser útil para identificar fotogramas de animación lentos que probablemente afecten la métrica de métricas web esenciales Interaction to Next Paint (INP), que mide la capacidad de respuesta, o para identificar otros bloqueos de IU que afectan la suavidad.

Estado de la API

Navegadores compatibles

  • 123
  • x
  • x
  • x

Después de una prueba de origen de Chrome 116 a Chrome 122, la API de LoAF se envió de Chrome 123.

API de Long Tasks

Navegadores compatibles

  • 58
  • 79
  • x
  • x

Origen

La API de Long Animation Frames es una alternativa a la API de Long Tasks, que está disponible en Chrome desde hace algún tiempo (desde Chrome 58). Como su nombre lo indica, la API de Long Task te permite monitorear tareas largas, que son tareas que ocupan el subproceso principal durante 50 milisegundos o más. Las tareas largas se pueden supervisar mediante la interfaz de PerformanceLongTaskTiming, con un PeformanceObserver:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'longtask', buffered: true });

Es probable que las tareas largas causen problemas de capacidad de respuesta. Si un usuario intenta interactuar con una página (por ejemplo, hacer clic en un botón o abrir un menú), pero el subproceso principal ya se ocupa de una tarea larga, la interacción del usuario se retrasa a la espera de que se complete esa tarea.

Para mejorar la capacidad de respuesta, se recomienda dividir las tareas largas. Si, en cambio, cada tarea larga se divide en una serie de múltiples tareas más pequeñas, puede permitir que se ejecuten tareas más importantes entre ellas para evitar retrasos significativos en la respuesta a las interacciones.

Por lo tanto, cuando se intenta mejorar la capacidad de respuesta, el primer esfuerzo suele ser ejecutar un registro del rendimiento y observar las tareas largas. Podría hacerlo a través de una herramienta de auditoría basada en labs, como Lighthouse (que tiene una auditoría para evitar tareas largas en el subproceso principal), o bien observando las tareas largas en las Herramientas para desarrolladores de Chrome.

Las pruebas basadas en laboratorios suelen ser un mal punto de partida para identificar problemas de capacidad de respuesta, ya que es posible que estas herramientas no incluyan interacciones. Cuando lo hacen, son un pequeño subconjunto de interacciones probables. Lo ideal sería medir las causas de las interacciones lentas en el campo.

Deficiencias de la API de Long Tasks

Medir tareas largas en el campo por medio de un observador de rendimiento solo es útil. En realidad, no brinda tanta información más allá del hecho de que se realizó una tarea larga y de cuánto tiempo llevó.

Las herramientas de supervisión de usuarios reales (RUM) suelen utilizar esto para determinar la cantidad o duración de tareas largas o identificar en qué páginas ocurren. Sin embargo, sin los detalles subyacentes de lo que causó la tarea larga, esto solo es de uso limitado. La API de Long Tasks solo tiene un modelo de atribución básico, que, en el mejor de los casos, solo indica el contenedor en el que ocurrió la tarea larga (el documento de nivel superior o un <iframe>), pero no la secuencia de comandos o la función que la llamó, como lo muestra una entrada típica:

{
  "name": "unknown",
  "entryType": "longtask",
  "startTime": 31.799999997019768,
  "duration": 136,
  "attribution": [
    {
      "name": "unknown",
      "entryType": "taskattribution",
      "startTime": 0,
      "duration": 0,
      "containerType": "window",
      "containerSrc": "",
      "containerId": "",
      "containerName": ""
    }
  ]
}

La API de Long Tasks también es una vista incompleta, ya que puede excluir algunas tareas importantes. Algunas actualizaciones, como la renderización, suceden en tareas separadas que idealmente deberían incluirse junto con la ejecución anterior que hizo que esa actualización midiera con precisión el “trabajo total” para esa interacción. Para obtener más detalles sobre las limitaciones de depender de las tareas, consulta la sección “¿Qué son las tareas largas?” de la explicación.

El último problema es que la medición de tareas largas solo informa sobre tareas individuales que tardan más de 50 milisegundos. Un fotograma de animación podría estar formado por varias tareas por debajo de este límite de 50 milisegundos, pero, en conjunto, seguir bloqueando la capacidad del navegador para renderizarse.

API de Long Animation Frames

Navegadores compatibles

  • 123
  • x
  • x
  • x

La API de Long Animation Frames (LoAF) es una nueva API que busca abordar algunas de las deficiencias de la API de Long Animation Frames para permitir a los desarrolladores obtener estadísticas más prácticas que ayuden a abordar problemas de capacidad de respuesta y mejorar INP.

Una buena capacidad de respuesta significa que una página responde rápidamente a las interacciones que se realizan con ella. Esto implica poder procesar las actualizaciones que necesite el usuario de manera oportuna y evitar que se bloqueen. En el caso del INP, se recomienda responder en 200 milisegundos o menos, pero para otras actualizaciones (por ejemplo, animaciones), incluso aunque sean demasiado largas.

La API de Long Animation Frames es un enfoque alternativo para medir el trabajo de bloqueo. En lugar de medir las tareas individuales, la API de Long Animation Frames, como su nombre lo indica, mide los fotogramas de animación largos. Un fotograma de animación largo ocurre cuando una actualización de renderización se retrasa más de 50 milisegundos (el mismo que el umbral de la API de Long Tasks).

Los fotogramas de animación largos se pueden observar de manera similar a las tareas largas con un PerformanceObserver, pero observando el tipo long-animation-frame:

const observer = new PerformanceObserver((list) => {
  console.log(list.getEntries());
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Los fotogramas de animación largos anteriores también se pueden consultar desde el Cronograma de rendimiento de la siguiente manera:

const loafs = performance.getEntriesByType('long-animation-frame');

Sin embargo, hay un maxBufferSize para las entradas de rendimiento después de las cuales se descartan las entradas más nuevas, por lo que se recomienda el enfoque PerformanceObserver. El tamaño del búfer long-animation-frame se establece en 200, al igual que para long-tasks.

Ventajas de mirar marcos en lugar de tareas

La ventaja clave de observar esto desde la perspectiva de un fotograma en lugar de una de tareas es que una animación larga puede estar compuesta por cualquier cantidad de tareas que, de forma acumulativa, dieron como resultado un fotograma de animación largo. Esto aborda el último punto mencionado anteriormente, en el que es posible que la API de Long Tasks no muestre la suma de muchas tareas más pequeñas que bloquean la renderización antes de un fotograma de animación.

Una ventaja adicional de esta vista alternativa sobre tareas largas es la capacidad de proporcionar desgloses de tiempo de todo el fotograma. En lugar de solo incluir un startTime y un duration, como la API de Long Tasks, la LoAF incluye un desglose mucho más detallado de las diversas partes de la duración del fotograma, que incluye lo siguiente:

  • startTime: Es la hora de inicio del fotograma de animación largo en relación con la hora de inicio de la navegación.
  • duration: Es la duración del fotograma de animación largo (sin incluir el tiempo de presentación).
  • renderStart: Es la hora de inicio del ciclo de renderización, que incluye devoluciones de llamada requestAnimationFrame, cálculos de estilo y diseño, devoluciones de llamada del observador de cambio de tamaño y de Intersection Observer.
  • styleAndLayoutStart: Es el comienzo del período dedicado a los cálculos de estilo y diseño.
  • firstUIEventTimestamp: Es la hora del primer evento de la IU (mouse/teclado, etc.) que se controlará durante este fotograma.
  • blockingDuration: Es la duración en milisegundos durante la que se bloqueó el fotograma de animación.

Estas marcas de tiempo permiten dividir el fotograma de animación largo en tiempos:

Tiempos Cálculo
Hora de inicio startTime
Hora de finalización startTime + duration
Duración del trabajo renderStart ? renderStart - startTime : duration
Duración del procesamiento renderStart ? (startTime + duration) - renderStart: 0
Renderización: Duración del diseño previo styleAndLayoutStart ? styleAndLayoutStart - renderStart : 0
Renderización: duración del estilo y el diseño styleAndLayoutStart ? (startTime + duration) - styleAndLayoutStart : 0

Para obtener más detalles sobre estos tiempos individuales, consulta la explicación, que proporciona información detallada sobre qué actividad contribuye a un fotograma de animación largo.

Mejor atribución

El tipo de entrada long-animation-frame incluye mejores datos de atribución de cada secuencia de comandos que contribuyó a la creación de un fotograma de animación largo.

Al igual que la API de Long Tasks, esto se proporcionará en un array de entradas de atribución, cada una de las cuales detalla lo siguiente:

  • Los elementos name y EntryType mostrarán script.
  • Es un valor invoker significativo que indica cómo se llamó a la secuencia de comandos (por ejemplo, 'IMG#id.onload', 'Window.requestAnimationFrame' o 'Response.json.then').
  • El invokerType del punto de entrada de la secuencia de comandos:
    • user-callback: Es una devolución de llamada conocida registrada desde una API de plataforma web (por ejemplo, setTimeout, requestAnimationFrame).
    • event-listener: Es un objeto de escucha de un evento de plataforma (por ejemplo, click, load o keyup).
    • resolve-promise: Es el controlador de una promesa de la plataforma (por ejemplo, fetch()). Ten en cuenta que en el caso de las promesas, todos los controladores de las mismas promesas se mezclan como una sola “secuencia de comandos”..
    • reject-promise: Según el resolve-promise, pero para el rechazo
    • classic-script: Evaluación de secuencia de comandos (por ejemplo, <script> o import())
    • module-script: Igual que classic-script, pero para secuencias de comandos de módulos.
  • Datos de sincronización separados para esa secuencia de comandos:
    • startTime: Hora en la que se invocó la función de entrada.
    • duration: Es la duración entre el startTime y el momento en que terminó de procesarse la microtarea posterior.
    • executionStart: Es el tiempo posterior a la compilación.
    • forcedStyleAndLayoutDuration: Es el tiempo total dedicado a procesar diseños o estilos forzados dentro de esta función (consulta hiperpaginación).
    • pauseDuration: Es el tiempo total dedicado a “pausar” operaciones síncronas (alerta, XHR síncrona).
  • Detalles de la fuente de la secuencia de comandos:
    • sourceURL: Es el nombre del recurso de la secuencia de comandos cuando está disponible (o está vacío si no se encuentra).
    • sourceFunctionName: Es el nombre de la función de la secuencia de comandos cuando está disponible (o está vacío si no se encuentra).
    • sourceCharPosition: Es la posición del carácter de la secuencia de comandos cuando está disponible (o -1 si no se encuentra).
  • windowAttribution: Es el contenedor (el documento de nivel superior o <iframe>) en el que se produjo el marco de animación largo.
  • window: Es una referencia a la ventana del mismo origen.

Cuando se proporcionan, las entradas de origen permiten a los desarrolladores saber exactamente cómo se llamó a cada secuencia de comandos en el marco de animación largo, hasta la posición del carácter en la secuencia de comandos de llamada. Esto proporciona la ubicación exacta en un recurso de JavaScript que dio como resultado el fotograma de animación largo.

Ejemplo de una entrada de rendimiento long-animation-frame

A continuación, se muestra un ejemplo completo de entrada de rendimiento long-animation-frame que contiene una sola secuencia de comandos:

{
  "blockingDuration": 0,
  "duration": 60,
  "entryType": "long-animation-frame",
  "firstUIEventTimestamp": 11801.099999999627,
  "name": "long-animation-frame",
  "renderStart": 11858.800000000745,
  "scripts": [
    {
      "duration": 45,
      "entryType": "script",
      "executionStart": 11803.199999999255,
      "forcedStyleAndLayoutDuration": 0,
      "invoker": "DOMWindow.onclick",
      "invokerType": "event-listener",
      "name": "script",
      "pauseDuration": 0,
      "sourceURL": "https://web.dev/js/index-ffde4443.js",
      "sourceFunctionName": "myClickHandler",
      "sourceCharPosition": 17796,
      "startTime": 11803.199999999255,
      "window": [Window object],
      "windowAttribution": "self"
    }
  ],
  "startTime": 11802.400000000373,
  "styleAndLayoutStart": 11858.800000000745
}

Como se puede observar, esto proporciona una cantidad de datos sin precedentes para que los sitios web puedan comprender la causa de las actualizaciones de representación retardada.

Habilitación de la API de Long Animation Frames

La API de Long Animation Frames está habilitada de forma predeterminada a partir de Chrome 123.

Cómo usar la API de Long Animation Frames en el campo

Las herramientas como Lighthouse, aunque son útiles para descubrir y reproducir problemas, son herramientas de lab que pueden pasar por alto aspectos importantes de la experiencia del usuario que solo los datos de campo pueden proporcionar. La API de Long Animation Frames se puede usar en el campo para recopilar datos contextuales importantes de las interacciones del usuario que la API de Long Tasks no pudo realizar. Esto puede ayudarte a detectar y reproducir problemas de interactividad que, de otra manera, no habrías descubierto.

A continuación, se enumeran algunas estrategias sugeridas, pero el equipo de Chrome está ansioso por recibir comentarios sobre esta API y sobre cómo los desarrolladores y proveedores de RUM se verían a sí mismos utilizando la información que proporciona la API.

Compatibilidad con la función de detección de la API de Long Animation Frames

Puedes usar el siguiente código para probar si se admite la API:

if (PerformanceObserver.supportedEntryTypes.includes('long-animation-frame')) {
  // Monitor LoAFs
}

En este caso, se puede usar la siguiente alternativa mientras los fotogramas de animación largos todavía no se admitan de forma predeterminada y se encuentren en este estado de transición:

if ('PerformanceLongAnimationFrameTiming' in window) {
  // Monitor LoAFs
}

Informar datos de animaciones largas a un extremo de análisis

Como se muestra, la entrada de rendimiento de la LOAF incluye información valiosa. Una estrategia sería supervisar todas las LOAF e transferir las que superan cierto umbral a un extremo de análisis para su posterior análisis:

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.duration > REPORTING_THRESHOLD_MS) {
      // Example here logs to console, but could also report back to analytics
      console.log(entry);
    }
  }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Dado que las entradas de los marcos de animación largos pueden ser bastante extensas, los desarrolladores deben decidir qué datos de la entrada deben enviarse a Analytics. Por ejemplo, los tiempos resumidos de la entrada y quizás los nombres de las secuencias de comandos, o algún otro conjunto mínimo de otros datos contextuales que puedan considerarse necesarios.

Observa los peores fotogramas de animación largos

Es posible que los sitios quieran recopilar datos en el fotograma (o fotogramas) más largo para reducir el volumen de datos que deben enviarse. Por lo tanto, sin importar cuántos fotogramas de animación largos experimente una página, solo se desvían los datos del peor de los fotogramas de animación, de los cinco o de la cantidad que sea absolutamente necesaria.

MAX_LOAFS_TO_CONSIDER = 10;
let longestBlockingLoAFs = [];

const observer = new PerformanceObserver(list => {
  longestBlockingLoAFs = longestBlockingLoAFs.concat(list.getEntries()).sort(
    (a, b) => b.blockingDuration - a.blockingDuration
  ).slice(0, MAX_LOAFS_TO_CONSIDER);
});
observer.observe({ type: 'long-animation-frame', buffered: true });

En el momento adecuado (idealmente en el evento visibilitychange), vuelve a enviar el píxel contador a Analytics. Para pruebas locales, puedes usar console.table de forma periódica:

console.table(longestBlockingLoAFs);

Vincula a la interacción de INP más larga

Como extensión de la observación de las peores LOAF, los marcos de la LOAF correspondientes a la entrada del INP se podrían usar como datos de atribución para brindar más detalles sobre cómo mejorar el INP.

Por el momento, no hay una API directa para vincular una entrada de INP con sus entradas de LoAF relacionadas, aunque es posible hacerlo en código comparando las horas de inicio y finalización de cada una (consulta esta secuencia de comandos de ejemplo).

Cómo informar fotogramas de animación largos con interacciones

Un enfoque alternativo que requiere menos código sería enviar siempre las entradas de LoAF más grandes (o las X superiores) en las que ocurrió una interacción durante el fotograma (que se puede detectar por la presencia de un valor firstUIEventTimestamp). En la mayoría de los casos, se incluirá la interacción con el INP de una visita determinada y, en casos excepcionales, cuando no sea así, se mostrarán interacciones largas que es importante corregir, ya que pueden ser interacciones de INP para otros usuarios.

El siguiente código registra todas las entradas de LoAF de más de 150 milisegundos en las que ocurrió una interacción durante la trama. Aquí se elige el número 150 porque es ligeramente inferior al umbral de INP “bueno” de 200 milisegundos. Puedes elegir un valor mayor o menor según tus necesidades.

const REPORTING_THRESHOLD_MS = 150;

const observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
      if (entry.duration > REPORTING_THRESHOLD_MS &&
        entry.firstUIEventTimestamp > 0
      ) {
        // Example here logs to console, but could also report back to analytics
        console.log(entry);
      }
    }
});
observer.observe({ type: 'long-animation-frame', buffered: true });

Cómo identificar patrones comunes en fotogramas de animación largos

Una estrategia alternativa sería observar las secuencias de comandos comunes que aparecen más en las entradas largas de marcos de animación. Los datos pueden informarse a nivel de un guion o de posición de los personajes para identificar a los reincidentes.

Esto puede funcionar especialmente bien para plataformas personalizables donde los temas o complementos que causan problemas de rendimiento podrían identificarse más fácilmente en varios sitios.

El tiempo de ejecución de secuencias de comandos comunes (o orígenes de terceros) en los marcos de animación largos podría resumirse e informarse para identificar los colaboradores habituales de los marcos de animación largos en un sitio o una colección de sitios. Por ejemplo, para analizar las URLs:

const observer = new PerformanceObserver(list => {
  const allScripts = list.getEntries().flatMap(entry => entry.scripts);
  const scriptSource = [...new Set(allScripts.map(script => script.sourceURL))];
  const scriptsBySource= scriptSource.map(sourceURL => ([sourceURL,
      allScripts.filter(script => script.sourceURL === sourceURL)
  ]));
  const processedScripts = scriptsBySource.map(([sourceURL, scripts]) => ({
    sourceURL,
    count: scripts.length,
    totalDuration: scripts.reduce((subtotal, script) => subtotal + script.duration, 0)
  }));
  processedScripts.sort((a, b) => b.totalDuration - a.totalDuration);
  // Example here logs to console, but could also report back to analytics
  console.table(processedScripts);
});

observer.observe({type: 'long-animation-frame', buffered: true});

Un ejemplo de este resultado es el siguiente:

(index) sourceURL count totalDuration
0 'https://example.consent.com/consent.js' 1 840
1 'https://example.com/js/analytics.js' 7 628
2 'https://example.chatapp.com/web-chat.js' 1 5

Cómo usar la API de Long Animation Frames en herramientas

La API también podría permitir herramientas adicionales para desarrolladores para la depuración local. Si bien algunas herramientas, como Lighthouse y las Herramientas para desarrolladores de Chrome, pudieron recopilar gran parte de estos datos con detalles de seguimiento de nivel inferior, contar con esta API de nivel superior podría permitir que otras herramientas accedan a estos datos.

Cómo mostrar datos de fotogramas de animación largos en Herramientas para desarrolladores

Puedes mostrar fotogramas de animación largos en las Herramientas para desarrolladores con la API de performance.measure(), que luego se muestran en el segmento de tiempos del usuario de Herramientas para desarrolladores en los seguimientos de rendimiento para mostrar dónde concentrar las iniciativas para mejorar el rendimiento:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    performance.measure('LoAF', {
      start: entry.startTime,
      end: entry.startTime + entry.duration,
    });
  }
});

observer.observe({ type: 'long-animation-frame', buffered: true });

Si esta API resulta útil a largo plazo, es probable que se incorpore a Herramientas para desarrolladores, pero el fragmento de código anterior permite que aparezca allí mientras tanto.

Cómo usar datos de fotogramas de animación largos en otras herramientas para desarrolladores

La extensión de Métricas web ha mostrado el valor en el registro de la información de depuración del resumen para diagnosticar problemas de rendimiento. Ahora que se lanzó la API, herramientas como esa podrían mostrar datos con mayor facilidad para ayudar a los desarrolladores a saber dónde concentrar sus esfuerzos. También planeamos agregar esta función a la biblioteca de JavaScript de métricas web en la versión 4.

Uso de datos de fotogramas de animación largos en herramientas de pruebas automatizadas

De manera similar, las herramientas de prueba automatizadas, quizás en canalizaciones de CI/CD, podrían mostrar detalles sobre posibles problemas de rendimiento al medir los fotogramas de animación largos mientras se ejecutan diversos paquetes de pruebas.

Preguntas frecuentes

Estas son algunas de las preguntas frecuentes sobre esta API:

¿Por qué no ampliar o iterar la API de Long Tasks?

Esta es una perspectiva alternativa para informar una medición similar, pero en última instancia diferente, de los posibles problemas de capacidad de respuesta. Es importante garantizar que los sitios que dependen de la API de Long Tasks existentes sigan funcionando para evitar interrumpir los casos de uso existentes.

Si bien la API de Long Tasks puede beneficiarse de algunas de las funciones de LoAF (como un mejor modelo de atribución), creemos que centrarse en los fotogramas en lugar de las tareas ofrece muchos beneficios que hacen que esta API sea fundamentalmente diferente de la API existente de Long Tasks.

¿Esto reemplazará la API de Long Tasks?

Si bien creemos que la API de Long Animation Frames es una API mejor y más completa para medir tareas largas, por el momento, no hay planes de dar de baja la API de Long Tasks.

Necesitamos tus comentarios

Puedes proporcionar tus comentarios en la Lista de problemas de GitHub o los errores en la implementación de la API de Chrome a través de la Herramienta de seguimiento de errores de Chrome.

Conclusión

La API de Long Animation Frames es una nueva y emocionante API con muchas ventajas potenciales en comparación con la API de Long Tasks anterior.

Está demostrado ser una herramienta clave para abordar los problemas de capacidad de respuesta según las mediciones del INP. INP es una métrica difícil de optimizar y esta API es una de las maneras en que el equipo de Chrome busca que los desarrolladores identifiquen y aborden los problemas con mayor facilidad.

Sin embargo, el alcance de la API de Long Animation Frames se extiende más allá de INP y puede ayudar a identificar otras causas de actualizaciones lentas que pueden afectar la fluidez general de la experiencia del usuario de un sitio web.

Agradecimientos

Imagen en miniatura de Henry Be en Unsplash.