1. Introducción
Una demostración y un codelab interactivos para aprender sobre la Interaction to Next Paint (INP).
Requisitos previos
- Conocimientos sobre el desarrollo de HTML y JavaScript
- Recomendación: Lee la documentación de INP.
Qué aprenderá
- Cómo la interacción entre las interacciones del usuario y tu manejo de esas interacciones afectan la capacidad de respuesta de la página
- Cómo reducir y eliminar las demoras para brindar una experiencia del usuario fluida
Requisitos
- Una computadora con la capacidad de clonar código de GitHub y ejecutar comandos de npm
- Un editor de texto
- Una versión reciente de Chrome para que funcionen todas las mediciones de interacción
2. Prepárate
Obtén y ejecuta el código
El código se encuentra en el repositorio web-vitals-codelabs.
- Clona el repositorio en tu terminal:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git - Recorre el directorio clonado:
cd web-vitals-codelabs/understanding-inp - Instala las dependencias:
npm ci - Inicia el servidor web:
npm run start - Visita http://localhost:5173/understanding-inp/ en tu navegador.
Descripción general de la app
En la parte superior de la página, se encuentra un contador de puntuación y un botón de incremento. Una demostración clásica de reactividad y capacidad de respuesta.

Debajo del botón, hay cuatro mediciones:
- INP: Es la puntuación de INP actual, que suele ser la peor interacción.
- Interacción: Es la puntuación de la interacción más reciente.
- FPS: Son los fotogramas por segundo del subproceso principal de la página.
- Temporizador: Es una animación de temporizador en ejecución para ayudar a visualizar los tirones.
Las entradas de FPS y Timer no son necesarias para medir las interacciones. Se agregan solo para que sea un poco más fácil visualizar la responsividad.
Probar
Intenta interactuar con el botón Increment y observa cómo aumenta la puntuación. ¿Cambian los valores de INP y de Interacción con cada incremento?
La INP mide el tiempo que transcurre desde el momento en que el usuario interactúa hasta que la página muestra la actualización renderizada al usuario.
3. Mide las interacciones con las Herramientas para desarrolladores de Chrome
Abre las Herramientas para desarrolladores desde el menú Más herramientas > Herramientas para desarrolladores, haciendo clic con el botón derecho en la página y seleccionando Inspeccionar, o usando una combinación de teclas.
Cambia al panel Rendimiento, que usarás para medir las interacciones.

A continuación, captura una interacción en el panel Rendimiento.
- Presiona Grabar.
- Interactúa con la página (presiona el botón Increment).
- Detén la grabación.
En la línea de tiempo resultante, encontrarás una pista de Interactions. Para expandirla, haz clic en el triángulo que se encuentra a la izquierda.

Aparecen dos interacciones. Acerca la segunda desplazándote o manteniendo presionada la tecla W.

Si colocas el cursor sobre la interacción, puedes ver que fue rápida, que no se dedicó tiempo a la duración del procesamiento y que se dedicó una cantidad mínima de tiempo a la demora de entrada y a la demora de presentación, cuyas duraciones exactas dependerán de la velocidad de tu máquina.
4. Objetos de escucha de eventos de larga duración
Abre el archivo index.js y quita el comentario de la función blockFor dentro del objeto de escucha de eventos.
Consulta el código completo: click_block.html
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
});
Guarda el archivo. El servidor verá el cambio y actualizará la página por ti.
Intenta interactuar con la página de nuevo. Ahora, las interacciones serán notablemente más lentas.
Seguimiento del rendimiento
Toma otra grabación en el panel Rendimiento para ver cómo se ve allí.

Lo que antes era una interacción breve ahora tarda un segundo completo.
Cuando colocas el cursor sobre la interacción, observa que el tiempo se dedica casi por completo a la "Duración del procesamiento", que es el tiempo que se tarda en ejecutar las devoluciones de llamada del objeto de escucha de eventos. Como la llamada de bloqueo blockFor se encuentra por completo dentro del objeto de escucha de eventos, ahí es donde se consume el tiempo.
5. Experimento: Duración del procesamiento
Prueba diferentes formas de reorganizar el trabajo del objeto de escucha de eventos para ver el efecto en la INP.
Primero, actualiza la IU
¿Qué sucede si intercambias el orden de las llamadas de JS, es decir, si primero actualizas la IU y luego bloqueas?
Consulta el código completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
¿Notaste que la IU apareció antes? ¿El orden afecta las puntuaciones de INP?
Intenta realizar un seguimiento y examinar la interacción para ver si hubo diferencias.
Objetos de escucha independientes
¿Qué sucede si mueves el trabajo a un objeto de escucha de eventos independiente? Actualiza la IU en un objeto de escucha de eventos y bloquea la página desde un objeto de escucha independiente.
Consulta el código completo: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
¿Cómo se ve ahora en el panel de rendimiento?
Diferentes tipos de eventos
La mayoría de las interacciones activarán muchos tipos de eventos, desde eventos de puntero o de teclas, hasta eventos de desplazamiento, de enfoque/desenfoque y sintéticos, como beforechange y beforeinput.
Muchas páginas reales tienen objetos de escucha para muchos eventos diferentes.
¿Qué sucede si cambias los tipos de eventos para los objetos de escucha de eventos? Por ejemplo, ¿reemplazar uno de los objetos de escucha de eventos click por pointerup o mouseup?
Consulta el código completo: diff_handlers.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
No hay actualizaciones de la IU
¿Qué sucede si quitas la llamada para actualizar la IU del objeto de escucha de eventos?
Consulta el código completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
6. Resultados del experimento de duración del procesamiento
Seguimiento del rendimiento: primero actualiza la IU
Consulta el código completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Si observas una grabación del panel Rendimiento en la que se hace clic en el botón, puedes ver que los resultados no cambiaron. Si bien se activó una actualización de la IU antes del código bloqueador, el navegador no actualizó lo que se pintó en la pantalla hasta que se completó el objeto de escucha de eventos, lo que significa que la interacción tardó poco más de un segundo en completarse.

Seguimiento del rendimiento: objetos de escucha independientes
Consulta el código completo: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
De nuevo, no hay diferencia funcional. La interacción sigue tardando un segundo completo.
Si acercas mucho la interacción de clic, verás que, de hecho, se llaman dos funciones diferentes como resultado del evento click.
Como se esperaba, la primera (actualizar la IU) se ejecuta con una rapidez increíble, mientras que la segunda tarda un segundo completo. Sin embargo, la suma de sus efectos da como resultado la misma interacción lenta para el usuario final.

Seguimiento del rendimiento: Diferentes tipos de eventos
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Estos resultados son muy similares. La interacción sigue siendo de un segundo completo; la única diferencia es que el objeto de escucha click más corto que solo actualiza la IU ahora se ejecuta después del objeto de escucha pointerup de bloqueo.

Seguimiento del rendimiento: sin actualización de la IU
Consulta el código completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- La puntuación no se actualiza, pero la página sí.
- Las animaciones, los efectos de CSS, las acciones predeterminadas de los componentes web (entrada de formulario), la entrada de texto y el destacado de texto se siguen actualizando.
En este caso, el botón pasa a un estado activo y vuelve cuando se hace clic en él, lo que requiere que el navegador lo pinte, lo que significa que todavía hay un INP.
Dado que el objeto de escucha de eventos bloqueó el subproceso principal durante un segundo, lo que impidió que se renderizara la página, la interacción aún tarda un segundo completo.
Si realizas una grabación del panel Rendimiento, se muestra la interacción prácticamente idéntica a las anteriores.

Conclusión
Cualquier código que se ejecute en cualquier objeto de escucha de eventos retrasará la interacción.
- Esto incluye los objetos de escucha registrados desde diferentes secuencias de comandos y el código de framework o biblioteca que se ejecuta en los objetos de escucha, como una actualización de estado que activa la renderización de un componente.
- No solo tu propio código, sino también todas las secuencias de comandos de terceros.
Es un problema común.
Por último, el hecho de que tu código no active una pintura no significa que no se esperará a que se completen los objetos de escucha de eventos lentos.
7. Experimento: Retraso de entrada
¿Qué sucede con el código de larga duración fuera de los objetos de escucha de eventos? Por ejemplo:
- Si tuvieras un
<script>de carga tardía que bloqueara la página de forma aleatoria durante la carga. - ¿Una llamada a la API, como
setInterval, que bloquea la página de forma periódica?
Intenta quitar el blockFor del objeto de escucha de eventos y agregarlo a un setInterval():
Consulta el código completo: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
¿Qué ocurre entonces?
8. Resultados del experimento de retraso de entrada
Consulta el código completo: input_delay.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
Si se registra un clic en un botón que ocurre mientras se ejecuta la tarea de bloqueo setInterval, se produce una interacción de larga duración, incluso sin que se realice ningún trabajo de bloqueo en la interacción en sí.
Estos períodos de larga duración suelen llamarse tareas largas.
Si colocas el cursor sobre la interacción en DevTools, podrás ver que el tiempo de interacción ahora se atribuye principalmente a la demora de entrada, no a la duración del procesamiento.

Ten en cuenta que no siempre afecta las interacciones. Si no haces clic mientras se ejecuta la tarea, es posible que tengas suerte. Esos estornudos "aleatorios" pueden ser una pesadilla para depurar cuando solo a veces causan problemas.
Una forma de detectarlos es medir las tareas largas (o los fotogramas de animación largos) y el Total Blocking Time.
9. Presentación lenta
Hasta ahora, analizamos el rendimiento de JavaScript a través del retraso de entrada o los objetos de escucha de eventos, pero ¿qué más afecta la renderización de la siguiente pintura?
Bueno, actualizando la página con efectos costosos.
Incluso si la actualización de la página se produce rápidamente, es posible que el navegador deba trabajar mucho para renderizarlos.
En el subproceso principal:
- Frameworks de IU que necesitan renderizar actualizaciones después de los cambios de estado
- Los cambios en el DOM o la activación de muchos selectores de consultas CSS costosos pueden activar muchos cambios de estilo, diseño y pintura.
Fuera del subproceso principal:
- Usa CSS para potenciar los efectos de GPU
- Cómo agregar imágenes de alta resolución muy grandes
- Usa SVG o Canvas para dibujar escenas complejas

Estos son algunos ejemplos que se encuentran comúnmente en la Web:
- Un sitio de SPA que vuelve a compilar todo el DOM después de hacer clic en un vínculo, sin detenerse para proporcionar un comentario visual inicial.
- Una página de búsqueda que ofrece filtros de búsqueda complejos con una interfaz de usuario dinámica, pero que ejecuta objetos de escucha costosos para hacerlo.
- Un botón de activación del modo oscuro que activa el estilo o el diseño de toda la página
10. Experimento: Retraso en la presentación
requestAnimationFrame tiene un funcionamiento lento
Simulemos un retraso de presentación largo con la API de requestAnimationFrame().
Mueve la llamada a blockFor a una devolución de llamada de requestAnimationFrame para que se ejecute después de que se devuelva el objeto de escucha de eventos:
Consulta el código completo: presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
¿Qué ocurre entonces?
11. Resultados del experimento de retraso en la presentación
Consulta el código completo: presentation_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
La interacción sigue durando un segundo, entonces, ¿qué sucedió?
requestAnimationFrame solicita una devolución de llamada antes de la siguiente pintura. Dado que la INP mide el tiempo desde la interacción hasta la siguiente pintura, el blockFor(1000) en el requestAnimationFrame sigue bloqueando la siguiente pintura durante un segundo completo.

Sin embargo, observa dos cosas:
- Cuando coloques el cursor sobre el evento, verás que todo el tiempo de interacción ahora se dedica a la "demora de presentación", ya que el bloqueo del subproceso principal se produce después de que se devuelve el objeto de escucha de eventos.
- La raíz de la actividad del subproceso principal ya no es el evento de clic, sino "Animation Frame Fired".
12. Diagnóstico de interacciones
En esta página de prueba, la capacidad de respuesta es muy visual, con las puntuaciones, los cronómetros y la IU del contador, pero cuando se prueba la página promedio, es más sutil.
Cuando las interacciones se ejecutan durante mucho tiempo, no siempre está claro cuál es el culpable. ¿Es:
- ¿Hay retraso de entrada?
- ¿Cuál es la duración del procesamiento de eventos?
- ¿Qué es el retraso en la presentación?
En cualquier página que desees, puedes usar las Herramientas para desarrolladores para medir la capacidad de respuesta. Para adquirir el hábito, prueba este flujo:
- Navega por la Web como lo harías normalmente.
- Presta atención al registro de interacciones en la vista de métricas en tiempo real del panel Rendimiento de las Herramientas para desarrolladores.
- Si ves una interacción con un rendimiento deficiente, intenta repetirla:
- Si no puedes repetirlo, usa el registro de interacción para obtener estadísticas.
- Si puedes repetirlo, graba un registro en el panel Rendimiento.
Todas las demoras
Intenta agregar un poco de todos estos problemas a la página:
Consulta el código completo: all_the_things.html
setInterval(() => {
blockFor(1000);
}, 3000);
button.addEventListener('click', () => {
blockFor(1000);
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
Luego, usa la consola y el panel de rendimiento para diagnosticar los problemas.
13. Experimento: Trabajo asíncrono
Dado que puedes iniciar efectos no visuales dentro de las interacciones, como realizar solicitudes de red, iniciar temporizadores o simplemente actualizar el estado global, ¿qué sucede cuando estos finalmente actualizan la página?
Siempre que se permita renderizar la siguiente pintura después de una interacción, incluso si el navegador decide que no necesita una nueva actualización de renderización, la medición de la interacción se detiene.
Para probar esto, sigue actualizando la IU desde el objeto de escucha de clics, pero ejecuta el trabajo de bloqueo desde el tiempo de espera.
Consulta el código completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
¿Qué hago?
14. Resultados del experimento de trabajo asíncrono
Consulta el código completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});

Ahora, la interacción es breve porque el subproceso principal está disponible inmediatamente después de que se actualiza la IU. La tarea de bloqueo larga se sigue ejecutando, solo que se ejecuta un tiempo después de la pintura, por lo que el usuario recibirá comentarios inmediatos de la IU.
Lección: Si no puedes quitarlo, al menos muévelo.
Métodos
¿Podemos mejorar el setTimeout fijo de 100 milisegundos? Es probable que aún queramos que el código se ejecute lo más rápido posible; de lo contrario, deberíamos haberlo quitado.
Objetivo:
- La interacción se ejecutará
incrementAndUpdateUI(). blockFor()se ejecutará lo antes posible, pero no bloqueará la siguiente renderización.- Esto genera un comportamiento predecible sin "tiempos de espera mágicos".
Algunas formas de lograrlo son las siguientes:
setTimeout(0)Promise.then()requestAnimationFramerequestIdleCallbackscheduler.postTask()
"requestPostAnimationFrame"
A diferencia de requestAnimationFrame por sí solo (que intentará ejecutarse antes de la siguiente pintura y, por lo general, seguirá generando una interacción lenta), requestAnimationFrame + setTimeout genera un polyfill simple para requestPostAnimationFrame, que ejecuta la devolución de llamada después de la siguiente pintura.
Consulta el código completo: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
Para mejorar la ergonomía, incluso puedes unificarlo en una promesa:
Consulta el código completo: raf+task2.html
async function nextPaint() {
return new Promise(resolve => afterNextPaint(resolve));
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await nextPaint();
blockFor(1000);
});
15. Interacciones múltiples (y clics de frustración)
Mover el trabajo de bloqueo largo puede ayudar, pero esas tareas largas aún bloquean la página, lo que afecta las interacciones futuras, así como muchas otras animaciones y actualizaciones de la página.
Vuelve a probar la versión de la página con trabajo de bloqueo asíncrono (o la tuya, si se te ocurrió tu propia variación para diferir el trabajo en el último paso):
Consulta el código completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
¿Qué sucede si haces clic varias veces rápidamente?
Seguimiento del rendimiento
Por cada clic, se pone en cola una tarea de un segundo de duración, lo que garantiza que el subproceso principal se bloquee durante un período considerable.

Cuando esas tareas largas se superponen con los clics nuevos que ingresan, se producen interacciones lentas, aunque el objeto de escucha de eventos en sí se devuelva casi de inmediato. Creamos la misma situación que en el experimento anterior con retrasos en la entrada. Solo que, esta vez, el retraso de entrada no proviene de un setInterval, sino del trabajo que activaron los objetos de escucha de eventos anteriores.
Estrategias
Lo ideal es quitar las tareas largas por completo.
- Quita todo el código innecesario, en especial las secuencias de comandos.
- Optimiza el código para evitar que se ejecuten tareas largas.
- Anula el trabajo inactivo cuando lleguen interacciones nuevas.
16. Estrategia 1: debounce
Una estrategia clásica. Cada vez que las interacciones se producen en una sucesión rápida y los efectos de procesamiento o de red son costosos, retrasa el inicio del trabajo a propósito para que puedas cancelar y reiniciar. Este patrón es útil para interfaces de usuario, como los campos de autocompletar.
- Usa
setTimeoutpara retrasar el inicio de un trabajo costoso con un temporizador, tal vez de 500 a 1, 000 milisegundos. - Guarda el ID del temporizador cuando lo hagas.
- Si llega una interacción nueva, cancela el temporizador anterior con
clearTimeout.
Consulta el código completo: debounce.html
let timer;
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
blockFor(1000);
}, 1000);
});
Seguimiento del rendimiento

A pesar de los múltiples clics, solo se ejecuta una tarea blockFor, que espera hasta que no haya habido ningún clic durante un segundo completo antes de ejecutarse. Para las interacciones que se producen en ráfagas, como escribir en una entrada de texto o los objetivos de elementos que se espera que reciban varios clics rápidos, esta es una estrategia ideal para usar de forma predeterminada.
17. Estrategia 2: Interrumpe el trabajo de larga duración
Aún existe la posibilidad de que se produzca un clic adicional justo después de que haya transcurrido el período de eliminación de rebotes, que se ubique en medio de esa tarea larga y que se convierta en una interacción muy lenta debido al retraso de entrada.
Idealmente, si se produce una interacción en medio de nuestra tarea, queremos pausar nuestro trabajo ocupado para que las nuevas interacciones se manejen de inmediato. ¿Cómo podemos hacerlo?
Existen algunas APIs, como isInputPending, pero , en general, es mejor dividir las tareas largas en fragmentos.
Muchos setTimeout
Primer intento: Haz algo simple.
Consulta el código completo: small_tasks.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
setTimeout(() => blockFor(100), 0);
});
});
Esto funciona permitiendo que el navegador programe cada tarea de forma individual, y la entrada puede tener una prioridad más alta.

Volvemos a tener cinco segundos completos de trabajo para cinco clics, pero cada tarea de un segundo por clic se dividió en diez tareas de 100 milisegundos. Como resultado, incluso con varias interacciones que se superponen con esas tareas, ninguna interacción tiene una demora de entrada de más de 100 milisegundos. El navegador prioriza los objetos de escucha de eventos entrantes por sobre el trabajo de setTimeout, y las interacciones siguen respondiendo.
Esta estrategia funciona especialmente bien cuando se programan puntos de entrada independientes, como cuando tienes un grupo de funciones independientes que necesitas llamar en el momento de carga de la aplicación. Cargar secuencias de comandos y ejecutar todo en el momento de la evaluación de la secuencia de comandos puede ejecutar todo en una tarea larga gigante de forma predeterminada.
Sin embargo, esta estrategia no funciona tan bien para separar el código con acoplamiento alto, como un bucle for que usa un estado compartido.
Ahora con yield()
Sin embargo, podemos aprovechar las async y await modernas para agregar fácilmente "puntos de productividad" a cualquier función de JavaScript.
Por ejemplo:
Consulta el código completo: yieldy.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldy(ms) {
const ms_per_part = 10;
const parts = ms / ms_per_part;
for (let i = 0; i < parts; i++) {
await schedulerDotYield();
blockFor(ms_per_part);
}
}
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
await blockInPiecesYieldy(1000);
});
Como antes, el subproceso principal se cede después de un fragmento de trabajo y el navegador puede responder a cualquier interacción entrante, pero ahora todo lo que se requiere es un await schedulerDotYield() en lugar de setTimeouts independientes, lo que lo hace lo suficientemente ergonómico como para usarlo incluso en medio de un bucle for.
Ahora con AbortContoller()
Eso funcionó, pero cada interacción programa más trabajo, incluso si se produjeron interacciones nuevas y es posible que haya cambiado el trabajo que se debe realizar.
Con la estrategia de eliminación de rebotes, cancelamos el tiempo de espera anterior con cada interacción nueva. ¿Podemos hacer algo similar aquí? Una forma de hacerlo es usar un AbortController():
Consulta el código completo: aborty.html
// Polyfill for scheduler.yield()
async function schedulerDotYield() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function blockInPiecesYieldyAborty(ms, signal) {
const parts = ms / 10;
for (let i = 0; i < parts; i++) {
// If AbortController has been asked to stop, abandon the current loop.
if (signal.aborted) return;
await schedulerDotYield();
blockFor(10);
}
}
let abortController = new AbortController();
button.addEventListener('click', async () => {
score.incrementAndUpdateUI();
abortController.abort();
abortController = new AbortController();
await blockInPiecesYieldyAborty(1000, abortController.signal);
});
Cuando se produce un clic, se inicia el bucle blockInPiecesYieldyAborty for que realiza el trabajo que se debe hacer, mientras que cede periódicamente el subproceso principal para que el navegador siga respondiendo a las interacciones nuevas.
Cuando se produce un segundo clic, el primer bucle se marca como cancelado con AbortController y se inicia un nuevo bucle blockInPiecesYieldyAborty. La próxima vez que se programe la ejecución del primer bucle, este notará que signal.aborted ahora es true y regresará de inmediato sin realizar más trabajo.

18. Conclusión
Dividir todas las tareas largas permite que un sitio responda a las interacciones nuevas. Esto te permite proporcionar comentarios iniciales con rapidez y, además, tomar decisiones, como anular el trabajo en curso. A veces, eso significa programar puntos de entrada como tareas independientes. A veces, eso significa agregar puntos de "yield" donde sea conveniente.
Recuerda
- La INP mide todas las interacciones.
- Cada interacción se mide desde la entrada hasta la siguiente pintura, es decir, la forma en que el usuario ve la capacidad de respuesta.
- El retraso de entrada, la duración del procesamiento de eventos y el retraso de presentación afectan en su totalidad la capacidad de respuesta de la interacción.
- Puedes medir la INP y los desgloses de interacciones con las Herramientas para desarrolladores fácilmente.
Estrategias
- No incluyas código de larga duración (tareas largas) en tus páginas.
- Mueve el código innecesario fuera de los objetos de escucha de eventos hasta después de la siguiente pintura.
- Asegúrate de que la actualización de renderización en sí sea eficiente para el navegador.