Medir · 3 min de lectura

Desmontar el INP: dónde se va el tiempo entre el clic y la imagen

Interaction to Next Paint mide lo rápido que una página responde de forma visible. La cifra tiene tres partes y cada una tiene otra causa.

Desde 2024, Interaction to Next Paint (INP) es una de las Core Web Vitals y sustituyó a First Input Delay. INP observa todos los clics, toques y pulsaciones de teclas durante una visita e informa aproximadamente de la más lenta, medida desde el momento de la entrada hasta la imagen en la que se ve la respuesta. El desplazamiento y el hover no cuentan.

Los límites del INP. La aguja marca la interacción de ejemplo de más abajo: 170 ms.

Se considera bueno hasta 200 ms, necesita mejorar hasta 500 ms y malo por encima, siempre referido al percentil 75 de todas las visitas.

Tres partes

Una interacción de 170 ms: ① el clic espera 60 ms a otra tarea, ② los handlers tardan 70 ms, ③ pasan 40 ms hasta la siguiente imagen.① retraso de entrada② procesamiento③ presentación
  1. Retraso de entrada: el tiempo hasta que puede empezar el primer handler. Aparece cuando el hilo principal está ocupado con otra cosa, típicamente una tarea larga.
  2. Procesamiento: lo que tardan todos los handlers de ese evento, es decir, tu propio código.
  3. Retraso de presentación: desde el final de los handlers hasta la siguiente imagen: callbacks de requestAnimationFrame, Style, Layout, Paint. Los cambios grandes en el DOM encarecen esta parte.

Medir

La Event Timing API entrega las tres partes de cada interacción. Las entradas con el mismo interactionId pertenecen a la misma interacción (por ejemplo pointerdown, pointerup y click):

new PerformanceObserver((list) => {
  for (const e of list.getEntries()) {
    if (!e.interactionId) continue; // no es un clic, toque ni tecla
    const inputDelay = e.processingStart - e.startTime;
    const processing = e.processingEnd - e.processingStart;
    const presentation = e.startTime + e.duration - e.processingEnd;
    console.log(e.name, e.duration, { inputDelay, processing, presentation });
  }
}).observe({ type: 'event', durationThreshold: 16, buffered: true });

durationThreshold fija a partir de qué duración se informan las entradas; 16 ms es el mínimo y, si no se indica, son 104 ms. La duración en sí se redondea a 8 ms, así que no conviene interpretar decimales.

Reducir cada parte por separado

Para el punto del medio funciona bien un patrón sencillo: primero la respuesta, después el trabajo:

button.addEventListener('click', async () => {
  button.classList.add('busy');  // respuesta visible enseguida
  await yieldToMain();           // ahora el navegador puede dibujar
  const result = compute();      // la parte cara, después
  render(result);
});

yieldToMain() es la misma pequeña función de la nota sobre tareas largas. Para el INP, la interacción termina con la primera imagen en la que el botón aparece ocupado. El cálculo posterior ya no cuenta para esa interacción, aunque, claro, retrasará la siguiente si es a su vez una tarea larga.

← Todas las notas