Acerca de este codelab
1. Introducción
Una demostración y un codelab interactivos para aprender sobre la Interaction to Next Paint (INP).
Requisitos previos
- Conocimiento del desarrollo de HTML y JavaScript
- Recomendación: Lee la documentación del INP.
Qué aprenderá
- Cómo la interacción entre las interacciones del usuario y el control de esas interacciones afectan la capacidad de respuesta de la página
- Cómo reducir y eliminar las demoras para ofrecer 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 repo en tu terminal:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
- Navega al 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 encuentran 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 medidas:
- INP: Es la puntuación actual del INP, 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: Una animación de temporizador en ejecución para ayudar a visualizar el jank.
Las entradas de FPS y Timer no son necesarias para medir las interacciones. Se agregan solo para facilitar un poco la visualización de la capacidad de respuesta.
Probar
Intenta interactuar con el botón Increment y observa cómo aumenta la puntuación. ¿Los valores de INP y Interaction cambian con cada incremento?
El INP mide cuánto tiempo transcurre desde el momento en que el usuario interactúa hasta que la página muestra la actualización renderizada.
3. Cómo medir las interacciones con las Herramientas para desarrolladores de Chrome
Abre las Herramientas para desarrolladores desde el menú Más herramientas > Herramientas para desarrolladores, haz clic con el botón derecho en la página y selecciona Inspeccionar o usa un atajo de teclado.
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 un segmento de Interacciones. Para expandirlo, haz clic en el triángulo que se encuentra en el lado izquierdo.
Aparecerán dos interacciones. Acerca la segunda imagen desplazándote o manteniendo presionada la tecla W.
Si colocas el cursor sobre la interacción, verás que fue rápida, que no se invirtió tiempo en la duración del procesamiento y que se invirtió una cantidad mínima de tiempo en la demora de entrada y la demora de presentación. La duración exacta dependerá de la velocidad de tu computadora.
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.
Ver 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.
Registro de rendimiento
Realiza otra grabación en el panel Rendimiento para ver cómo se ve allí.
Lo que antes era una interacción breve ahora lleva 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. Dado que la llamada de bloqueo blockFor
se encuentra completamente dentro del objeto de escucha de eventos, ahí es donde se consume el tiempo.
5. Experiment: processing duration
Prueba diferentes formas de reorganizar el trabajo del objeto de escucha de eventos para ver el efecto en el INP.
Actualiza la IU primero
¿Qué sucede si intercambias el orden de las llamadas a JS: primero actualizas la IU y, luego, bloqueas?
Ver 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 del INP?
Intenta tomar un registro y examinar la interacción para ver si hubo alguna diferencia.
Objetos de escucha separados
¿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 teclado hasta eventos de desplazamiento, de enfoque o desenfoque, y eventos 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ías 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);
});
Sin actualización 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
Registro de rendimiento: Actualiza la IU primero
Ver el código completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Si observas una grabación del panel Rendimiento del clic en el botón, verás que los resultados no cambiaron. Si bien se activó una actualización de la IU antes del código de bloqueo, el navegador no actualizó lo que se pintó en la pantalla hasta después de 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.
Registro de rendimiento: Objetos de escucha separados
Consulta el código completo: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
De nuevo, no hay diferencias funcionales. La interacción sigue tardando un segundo completo.
Si acercas 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, que actualiza la IU, se ejecuta muy rápido, mientras que la segunda tarda un segundo completo. Sin embargo, la suma de sus efectos genera la misma interacción lenta para el usuario final.
Registro de rendimiento: Diferentes tipos de eventos
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('pointerup', () => {
blockFor(1000);
});
Estos resultados son muy similares. La interacción sigue durando un segundo completo. La única diferencia es que el objeto de escucha click
más corto y que solo actualiza la IU ahora se ejecuta después del objeto de escucha pointerup
de bloqueo.
Registro de rendimiento: No hay 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 CSS, las acciones predeterminadas de los componentes web (entrada de formularios), la entrada de texto y el resaltado de texto se siguen actualizando.
En este caso, el botón pasa a un estado activo y vuelve a su estado original cuando se hace clic en él, lo que requiere que el navegador realice una pintura, lo que significa que aún hay un INP.
Dado que el objeto de escucha de eventos bloqueó el subproceso principal durante un segundo, lo que impidió que se pintara la página, la interacción sigue tardando un segundo completo.
Si grabas un panel de 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 todos los scripts de terceros.
Es un problema común.
Por último, el hecho de que tu código no active una pintura no significa que una pintura no esperará a que se completen los detectores de eventos lentos.
7. Experiment: input delay
¿Qué sucede con el código de ejecución prolongada fuera de los objetos de escucha de eventos? Por ejemplo:
- Si tenías un
<script>
que se cargaba tarde y bloqueaba 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();
});
Grabar un clic en un botón que ocurre mientras se ejecuta la tarea de bloqueo de setInterval
genera una interacción de larga duración, incluso si no se realiza ningún trabajo de bloqueo en la interacción.
Estos períodos de ejecución prolongada suelen llamarse tareas largas.
Si colocas el cursor sobre la interacción en Herramientas para desarrolladores, 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 cuando se ejecuta la tarea, es posible que tengas suerte. Estos estornudos "aleatorios" pueden ser una pesadilla para depurar cuando solo a veces causan problemas.
Una forma de detectarlas es medir las tareas largas (o Long Animation Frames) y el Tiempo total de bloqueo.
9. Presentación lenta
Hasta ahora, analizamos el rendimiento de JavaScript a través de la demora de entrada o los detectores de eventos, pero ¿qué más afecta la siguiente pintura de renderización?
Bueno, actualizamos la página con efectos costosos.
Incluso si la actualización de la página se realiza rápidamente, es posible que el navegador aún deba trabajar mucho para renderizarla.
En el subproceso principal, haz lo siguiente:
- 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 muchas operaciones de diseño, pintura y estilo.
Fuera del subproceso principal:
- Uso de CSS para potenciar los efectos de la GPU
- Cómo agregar imágenes de alta resolución muy grandes
- Cómo usar SVG/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 comentarios visuales iniciales.
- Una página de búsqueda que ofrece filtros de búsqueda complejos con una interfaz de usuario dinámica, pero ejecuta detectores costosos para hacerlo.
- Un botón de activación del modo oscuro que activa el diseño y el diseño de toda la página
10. Experiment: presentation delay
requestAnimationFrame
tiene un funcionamiento lento
Simulemos una demora prolongada en la presentación 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 regrese el objeto de escucha de eventos:
Ver 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
Ver 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 el 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, ten en cuenta dos aspectos:
- Cuando coloques el cursor sobre el gráfico, verás que todo el tiempo de interacción ahora se dedica a la "demora en la presentación", ya que el bloqueo del subproceso principal se produce después de que regresa el objeto de escucha de eventos.
- La raíz de la actividad del subproceso principal ya no es el evento de clic, sino "Se activó el fotograma de animación".
12. Diagnóstico de interacciones
En esta página de prueba, la capacidad de respuesta es muy visual, con las puntuaciones, los temporizadores y la IU del contador, pero, cuando se prueba la página promedio, es más sutil.
Cuando las interacciones se extienden, no siempre está claro cuál es el problema. ¿Es lo siguiente?
- ¿Hay retraso de entrada?
- ¿Cuál es la duración del procesamiento de eventos?
- ¿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 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 esos efectos finalmente actualizan la página?
La medición de la interacción se detiene siempre que se permita el procesamiento de la siguiente pintura después de una interacción, incluso si el navegador decide que no necesita una nueva actualización de procesamiento.
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);
});
La interacción ahora es breve porque el subproceso principal está disponible inmediatamente después de que se actualiza la IU. La tarea de bloqueo prolongado sigue ejecutándose, solo que lo hace 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 hacerlo mejor que un setTimeout
fijo de 100 milisegundos? Probablemente, aún queramos que el código se ejecute lo más rápido posible; de lo contrario, simplemente lo habríamos quitado.
Objetivo:
- La interacción ejecutará
incrementAndUpdateUI()
. blockFor()
se ejecutará lo antes posible, pero no bloqueará la siguiente pintura.- Esto genera un comportamiento predecible sin "tiempos de espera mágicos".
Algunas formas de lograrlo son las siguientes:
setTimeout(0)
Promise.then()
requestAnimationFrame
requestIdleCallback
scheduler.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
crea un polyfill simple para requestPostAnimationFrame
, que ejecuta la devolución de llamada después de la siguiente pintura.
Ver el código completo: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
Para mayor ergonomía, incluso puedes incluirlo 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. Varias interacciones (y clics de frustración)
Mover el trabajo de bloqueo prolongado puede ayudar, pero esas tareas largas siguen bloqueando 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 trabajo de bloqueo asíncrono de la página (o la tuya propia si creaste tu propia variación para aplazar 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?
Registro de rendimiento
Para cada clic, se pone en cola una tarea de un segundo de duración, lo que garantiza que el subproceso principal esté bloqueado durante un tiempo considerable.
Cuando esas tareas largas se superponen con los clics nuevos que ingresan, se producen interacciones lentas, aunque el objeto de escucha de eventos se muestre casi de inmediato. Creamos la misma situación que en el experimento anterior con demoras de entrada. Solo que, esta vez, la demora de entrada no proviene de un setInterval
, sino del trabajo que activaron los objetos de escucha de eventos anteriores.
Estrategias
Lo ideal sería quitar las tareas largas por completo.
- Quita todo el código innecesario, en especial las secuencias de comandos.
- Optimiza el código para evitar la ejecución de tareas prolongadas.
- Anula el trabajo inactivo cuando llegan interacciones nuevas.
16. Estrategia 1: Reducción de rebotes
Una estrategia clásica. Cuando las interacciones llegan en rápida sucesión y los efectos de procesamiento o de red son costosos, retrasa iniciar el trabajo a propósito para que puedas cancelar y reiniciar. Este patrón es útil para interfaces de usuario, como los campos de autocompletado.
- Usa
setTimeout
para 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 nueva interacción, 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);
});
Registro de rendimiento
A pesar de los múltiples clics, solo se ejecuta una tarea de blockFor
, que espera hasta que no haya habido clics durante un segundo completo antes de ejecutarse. Para las interacciones que se producen en ráfagas, como escribir en una entrada de texto o elementos objetivo que se espera que reciban varios clics rápidos, esta es una estrategia ideal para usar de forma predeterminada.
17. Estrategia 2: Interrumpir el trabajo de larga duración
Aun así, existe la posibilidad de que se produzca otro clic justo después de que haya pasado el período de rebote, que se produzca en medio de esa tarea larga y que se convierta en una interacción muy lenta debido a la demora de entrada.
Lo ideal sería que, si una interacción se produce en medio de nuestra tarea, detengamos el trabajo ocupado para que las nuevas interacciones se manejen de inmediato. ¿Cómo podemos hacerlo?
Existen algunas APIs como isInputPending
, pero generalmente es mejor dividir las tareas largas en fragmentos.
Muchos setTimeout
s
Primer intento: Haz algo simple.
Ver 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 mayor prioridad.
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 superior a 100 milisegundos. El navegador prioriza los objetos de escucha de eventos entrantes por sobre el trabajo de setTimeout
, y las interacciones siguen siendo responsivas.
Esta estrategia funciona especialmente bien cuando se programan puntos de entrada separados, por ejemplo, si tienes un conjunto de funciones independientes que necesitas llamar en el tiempo 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 gigante y larga de forma predeterminada.
Sin embargo, esta estrategia no funciona tan bien para separar el código estrechamente acoplado, como un bucle for
que usa un estado compartido.
Ahora con yield()
Sin embargo, podemos aprovechar las funciones async
y await
modernas para agregar fácilmente "puntos de rendimiento" a cualquier función de JavaScript.
Por ejemplo:
Ver 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 setTimeout
s separados, 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 que podrían haber 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 necesario y, periódicamente, cede el subproceso principal para que el navegador siga respondiendo a las nuevas interacciones.
Cuando llega 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, 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 rápidamente y tomar decisiones, como abortar el trabajo en curso. A veces, eso significa programar los puntos de entrada como tareas separadas. A veces, eso significa agregar puntos de "rendimiento" donde sea conveniente.
Recuerda
- El INP mide todas las interacciones.
- Cada interacción se mide desde la entrada hasta la siguiente pintura, que es 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 todos afectan la capacidad de respuesta de la interacción.
- Puedes medir el INP y los desgloses de las interacciones con Herramientas para desarrolladores fácilmente.
Estrategias
- No tengas 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 la renderización sea eficiente para el navegador.