wandres.dev
EL HILO PRINCIPAL · Long tasks y bloqueo

El TBT y su relación con el INP: dos métricas del mismo problema

Cómo se calcula exactamente el tiempo total de bloqueo, por qué es una métrica de laboratorio y no de campo, qué correlación tiene con el INP y en qué casos concretos las dos discrepan.

⏱ 17 min

El tiempo total de bloqueo es la métrica que mejor resume el coste del JavaScript durante la carga, y es la que más peso tiene en las auditorías automáticas. El INP mide la respuesta a interacciones reales durante toda la sesión. Las dos miden consecuencias del mismo problema —un hilo principal ocupado— y por eso correlacionan bien, pero no son la misma cosa, y los casos en que discrepan son precisamente los que revelan de qué tipo es tu problema.

🎯 Al terminar esta lección sabrás
  • Calcular el TBT a mano a partir de una lista de tareas.
  • Explicar por qué el TBT no se puede medir en campo.
  • Interpretar las cuatro combinaciones posibles de TBT bueno o malo con INP bueno o malo.
  • Elegir cuál de las dos métricas gobierna una decisión concreta.

El cálculo, paso a paso

El tiempo total de bloqueo es la suma, sobre todas las tareas de una ventana de medición, del exceso de cada tarea por encima de 50 milisegundos.

La ventana estándar va desde el primer pintado con contenido hasta el tiempo hasta interactivo. La razón de empezar en el primer pintado es que antes de ese momento no hay nada en pantalla y el usuario no va a interactuar con nada.

Un ejemplo completo:

Tarea Duración Porción bloqueante
Evaluar el bundle principal 320 ms 270 ms
Hidratar el árbol 180 ms 130 ms
Analítica de terceros 62 ms 12 ms
Formatear las fechas del listado 48 ms 0 ms
Registrar oyentes 41 ms 0 ms
Aplicar el tema 95 ms 45 ms
TBT 457 ms

Dos observaciones que salen directamente de la tabla y que explican el comportamiento de la métrica.

Las tareas por debajo de 50 milisegundos aportan cero. La de 48 y la de 41 suman 89 milisegundos de hilo ocupado y no se contabilizan. Es intencionado —el umbral existe para no penalizar el trabajo inevitable— y produce el punto ciego que ya conoces: mucho trabajo troceado en porciones de 45 milisegundos da un TBT excelente y una página que se siente pastosa.

La métrica premia enormemente partir las tareas grandes. Partir la tarea de 320 milisegundos en siete de 46 lleva su contribución de 270 a cero. No has hecho menos trabajo; has cambiado por completo la métrica. Y esto no es hacer trampa: es exactamente lo que la métrica pretende incentivar, porque siete tareas de 46 milisegundos dejan seis huecos donde el navegador puede atender al usuario, y la tarea de 320 no deja ninguno.

Los umbrales de referencia en un dispositivo móvil emulado son: por debajo de 200 milisegundos es bueno, entre 200 y 600 es mejorable, por encima de 600 es malo. Y en las auditorías automáticas el TBT es el componente con más peso de la puntuación de rendimiento, en torno al 30 por ciento, lo cual explica por qué una página con muchas imágenes bien optimizadas y un bundle enorme saca mala nota.

Por qué no se puede medir en campo

El TBT necesita conocer la duración de todas las tareas, incluidas las que están por debajo del umbral de 50 milisegundos. Y la API de tareas largas, que es la única disponible en el navegador del usuario, solo informa de las que lo superan.

Esto no es un descuido: exponer todas las tareas permitiría medir con precisión el tiempo de ejecución de código de otros orígenes, lo cual abre la puerta a ataques de canal lateral. La limitación es deliberada y no va a levantarse.

La consecuencia es que el TBT es una métrica de laboratorio, no de campo. Se mide con un perfilador o con una herramienta de auditoría que controla el navegador. En producción, con usuarios reales, no se puede calcular.

Y de ahí sale la existencia del INP como métrica de campo: mide lo que sí se puede medir en el navegador del usuario, que es cuánto tarda una interacción concreta en producir el siguiente pintado. No mide el hilo directamente; mide su consecuencia observable.

Hay una aproximación de campo al TBT que algunas herramientas usan y conviene saber que existe y que es solo eso, una aproximación: sumar el exceso sobre 50 de las tareas largas reportadas por la API. Da un número correlacionado y sistemáticamente inferior al TBT real, porque le falta todo lo que está por debajo del umbral.

// Aproximacion de campo al TBT. Util para comparar, no para cumplir un umbral.
let tbtAprox = 0;
new PerformanceObserver((lista) => {
  for (const t of lista.getEntries()) tbtAprox += Math.max(0, t.duration - 50);
}).observe({ type: 'longtask', buffered: true });

Las cuatro combinaciones

Aquí está el valor diagnóstico de tener las dos métricas. Cada combinación apunta a un tipo de problema distinto.

TBT bueno, INP bueno. No hay problema de hilo principal. Si la página se siente lenta, mira la red, el servidor o el CLS.

TBT malo, INP bueno. El problema está concentrado en la carga. Bundle grande, hidratación cara, trabajo de arranque. Una vez arrancada, la aplicación responde bien. Es el perfil típico de una aplicación con marco bien escrita y demasiado código inicial. La solución es troceado y menos trabajo en el arranque.

TBT bueno, INP malo. Arranca ligera y se atasca al usar. El problema está en los manejadores de eventos, en el renderizado tras la interacción o en trabajo periódico. Es el perfil de las aplicaciones que crecen: cada funcionalidad añade un oyente, y a los dos años un clic dispara catorce. Es el caso que más se ve y el que las auditorías automáticas no detectan, porque solo miran la carga.

TBT malo, INP malo. Problema estructural de cantidad de JavaScript. Aquí no hay una optimización puntual; hay que reducir.

La tabla anterior es la herramienta de triaje más útil de este nivel, y se construye con dos datos que ya tienes: el TBT de tu auditoría de laboratorio y el percentil 75 del INP de tus datos de campo.

Un TBT excelente con un INP terrible es casi siempre el mismo bug, y está en un oyente pasivo mal puesto

La combinación de arranque impecable y respuesta pésima tiene un puñado de causas típicas, y una destaca por frecuencia hasta el punto de que merece la pena buscarla primero.

Los oyentes de eventos de alta frecuencia sin marcar como pasivos. scroll, wheel, touchmove, pointermove. El navegador, cuando hay un oyente registrado para uno de esos eventos sin passive: true, no puede empezar a desplazar hasta que el oyente haya terminado, porque podría llamar a preventDefault(). Con un oyente que tarda 12 milisegundos y eventos que llegan cada 8, la cola se acumula y el desplazamiento se convierte en una sucesión de tirones.

En el perfil esto no aparece como una tarea larga: aparece como cientos de tareas cortas seguidas, todas por debajo del umbral. TBT cero. INP catastrófico. Y la corrección es una palabra:

// Bloquea el desplazamiento hasta que el manejador termina
window.addEventListener('scroll', alDesplazar);

// No lo bloquea: el navegador sabe que no vas a cancelar el evento
window.addEventListener('scroll', alDesplazar, { passive: true });

En navegadores modernos, touchstart y touchmove sobre window, document y body son pasivos por defecto precisamente por este problema, pero wheel y los oyentes sobre otros elementos no lo son, y ahí sigue el fallo.

Las otras tres causas frecuentes de este perfil, por orden:

Trabajo periódico que nadie recuerda haber puesto. Un setInterval de sondeo cada segundo que consulta el servidor y vuelve a renderizar una parte del árbol. En reposo es invisible; cuando coincide con una interacción, la retrasa. Búscalos: grep -rn "setInterval" src/ suele producir revelaciones.

Observadores caros. Un ResizeObserver o un IntersectionObserver cuya devolución de llamada hace trabajo de layout, disparándose en cada fotograma durante un desplazamiento.

El propio manejador haciendo demasiado. Un clic que valida, guarda en almacenamiento local, envía telemetría, actualiza tres partes del estado y vuelve a renderizar. Cada pieza es rápida; la suma no. Es el caso que resuelve el patrón de separar la respuesta visual del trabajo pesado, en optimizar INP.

La forma de distinguir cuál de las cuatro tienes, en dos minutos: graba un perfil mientras interactúas, no durante la carga, y mira la forma del hilo principal. Muchas tareas cortas y densas apuntan a oyentes de alta frecuencia. Una tarea larga aislada tras el clic apunta al manejador. Tareas periódicas regulares apuntan a un intervalo.

Cuál de las dos manda

La regla de decisión es simple una vez que se entiende qué mide cada una.

Para decidir si un cambio de código se despliega, manda el TBT, porque se puede medir de forma determinista en integración continua, antes de que ningún usuario lo sufra.

Para decidir en qué trabajar, manda el INP, porque es lo que experimentan los usuarios reales, en sus dispositivos, en sus flujos.

Y para diagnosticar, hacen falta las dos, más el perfil que las explica.

Un error de gestión que conviene evitar: perseguir la puntuación de la auditoría automática como objetivo. Esa puntuación pondera el TBT y otras métricas de laboratorio con pesos fijos, medidos en una máquina virtual en condiciones simuladas. Es un excelente detector de regresiones y un mal objetivo, porque optimizar para ella lleva a decisiones que mejoran el número sin mejorar la experiencia. El objetivo son los percentiles de campo; la auditoría es el guardarraíl.

⚔️ Reto práctico

Consigue las dos cifras de tu producto: el TBT de una auditoría en el dispositivo de referencia y el percentil 75 del INP de tus datos de campo, segmentado por clase de dispositivo. Colócate en una de las cuatro casillas. Si estás en la de TBT bueno e INP malo, graba un perfil durante una interacción real y busca las cuatro causas de la lista por orden; en la mayoría de los casos la primera aparece antes de la tercera.