1. Introducción
Una demostración interactiva y un codelab para aprender sobre Interaction to Next Paint (INP).
Requisitos previos
- Conocimiento sobre desarrollo en HTML y JavaScript
- Recomendación: Lee la documentación de INP.
Qué aprenderá
- Cómo la interacción de las interacciones de los usuarios y tu manejo de estas 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 desde GitHub y ejecutar comandos npm.
- Un editor de texto
- Una versión reciente de Chrome para que funcionen todas las mediciones de interacción.
2. Prepárate
Cómo obtener y ejecutar el código
El código se encuentra en el repositorio de web-vitals-codelabs
.
- Clona el repositorio en tu terminal:
git clone https://github.com/GoogleChromeLabs/web-vitals-codelabs.git
- Pasa 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 el contador Puntuación y el botón Incremento. ¡Una demostración clásica de reactividad y capacidad de respuesta!
Debajo del botón, hay cuatro medidas:
- INP: la puntuación de INP actual, que suele ser la peor interacción.
- Interacción: La puntuación de la interacción más reciente
- FPS: Los fotogramas por segundo del subproceso principal de la página.
- Temporizador: Es una animación de temporizador en ejecución que ayuda a visualizar el bloqueo.
Las entradas de FPS y Timer no son necesarias para medir interacciones. Se agregan solo para facilitar 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 e Interaction cambian con cada incremento?
INP mide el tiempo que transcurre desde el momento en que el usuario interactúa hasta que la página realmente le muestra la actualización procesada.
3. Medición de interacciones con las Herramientas para desarrolladores de Chrome
Abre las Herramientas para desarrolladores desde Más herramientas > En el menú Herramientas para desarrolladores, haz clic con el botón derecho en la página y selecciona Inspeccionar, o bien usa 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 el cronograma resultante, encontrarás un segmento de Interacciones. Para expandirlo, haz clic en el triángulo que aparece a la izquierda.
Aparecerán dos interacciones. Para acercar el segundo, desplázate o mantén presionada la tecla W.
Si colocas el cursor sobre la interacción, verás que la interacción fue rápida, no invirtió tiempo en la duración del procesamiento ni en retraso de entrada y retraso de presentación. La duración exacta dependerá de la velocidad de la 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.
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 nuevamente. Ahora las interacciones serán notablemente más lentas.
Seguimiento del rendimiento
Realiza otra grabación en el panel Rendimiento para ver cómo se ve aquí.
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 "Duración del procesamiento", que es la cantidad de tiempo necesaria para ejecutar las devoluciones de llamada del objeto de escucha de eventos. Como la llamada de bloqueo a blockFor
se encuentra completamente dentro del objeto de escucha de eventos, el tiempo pasa allí.
5. Experimento: duración del procesamiento
Prueba formas de reorganizar el trabajo del objeto de escucha de eventos para ver el efecto en el INP.
Primero actualiza la IU
¿Qué sucede si cambias el orden de las llamadas de js? Primero, actualiza la IU y, luego, realiza el bloqueo.
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 del INP?
Intenta hacer un seguimiento y examinar la interacción para ver si hubo alguna diferencia.
Objetos de escucha separados
¿Qué pasa si mueves el trabajo a un objeto de escucha de eventos separado? Actualiza la IU en un objeto de escucha de eventos y bloquea la página en un objeto de escucha independiente.
Ver 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 punteros o eventos clave hasta desplazamiento, enfoque/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, ¿reemplazas 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 IU
¿Qué sucede si quitas la llamada para actualizar la IU del objeto de escucha de eventos?
Ver código completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
6. Procesando los resultados del experimento de duración
Seguimiento del rendimiento: Primero actualiza la IU
Consulta el código completo: ui_first.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
blockFor(1000);
});
Si miras una grabación del panel Rendimiento cuando haces clic en el botón, puedes ver que los resultados no cambiaron. Si bien se activó una actualización de IU antes del código de bloqueo, el navegador no actualizó lo que se pintó en la pantalla hasta después de que se completara el objeto de escucha de eventos, lo que significa que la interacción aún tardó un poco más de un segundo en completarse.
Seguimiento del rendimiento: objetos de escucha separados
Ver el código completo: two_click.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
});
button.addEventListener('click', () => {
blockFor(1000);
});
Una vez más, funcionalmente no hay diferencia. La interacción aún tarda un segundo completo.
Si te acercas mucho a la interacción del clic, verás que se llama a dos funciones diferentes como resultado del evento click
.
Como era de esperar, la primera (actualizar la IU) se ejecuta increíblemente rápido, 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 aún es de un segundo completo; La única diferencia es que el objeto de escucha click
más corto solo para actualizaciones de IU ahora se ejecuta después del objeto de escucha pointerup
que genera el bloqueo.
Seguimiento de rendimiento: sin actualización de la IU
Ver código completo: no_ui.html
button.addEventListener('click', () => {
blockFor(1000);
// score.incrementAndUpdateUI();
});
- No se actualiza la puntuación, pero la página sigue funcionando.
- Las animaciones, los efectos CSS, las acciones de componentes web predeterminadas (entrada de formulario), la entrada de texto y los elementos destacados de texto siguen actualizándose.
En este caso, el botón pasa a un estado activo y vuelve a un estado activo cuando se hace clic en él, lo que requiere que el navegador pinte, lo que significa que aún hay un INP.
Como el objeto de escucha de eventos bloqueó el subproceso principal por un segundo y impidió que se pinte la página, la interacción aún tarda un segundo completo.
Cuando grabas el panel Rendimiento, la interacción es prácticamente idéntica a la de antes.
Comida para llevar
Cualquier código que se ejecute en cualquier objeto de escucha de eventos retrasará la interacción.
- Eso incluye objetos de escucha registrados a partir de diferentes secuencias de comandos y código de framework o biblioteca que se ejecuta en objetos de escucha, como una actualización de estado que activa la renderización de un componente.
- No solo su 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 esta no estará esperando a que se completen los objetos de escucha de eventos lentos.
7. Experimento: retraso de entrada
¿Qué ocurre con el código de larga duración fuera de los objetos de escucha de eventos? Por ejemplo:
- Si tenías una
<script>
de carga tardía que bloqueó 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 cuando no se realiza trabajo de bloqueo en la interacción en sí.
Estos períodos de larga duración a menudo se denominan 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 al retraso de entrada, no a la duración del procesamiento.
Ten en cuenta que no siempre afecta las interacciones. Si no haces clic cuando se está ejecutando la tarea, es posible que tengas suerte. Es algo tan "aleatorio" estornudos puede ser una pesadilla cuando solo causan problemas a veces.
Una forma de hacerlo es medir las tareas largas (o Fotogramas de animación largos) y el Tiempo de bloqueo total.
9. Presentación lenta
Hasta ahora, analizamos el rendimiento de JavaScript mediante la demora de entrada o los objetos de escucha de eventos, pero ¿qué más afecta a la renderización de la siguiente pintura?
Actualizar la página con efectos costosos.
Incluso si la página se actualiza rápidamente, es posible que el navegador deba hacer todo lo posible para procesarlas.
En el subproceso principal:
- Frameworks de la IU que deben renderizar actualizaciones después de los cambios de estado
- Los cambios de DOM o la activación de muchos selectores de consultas de CSS costosos pueden desencadenar muchos estilos, diseños y pinturas.
Fuera del subproceso principal:
- Cómo usar CSS para potenciar los efectos de la GPU
- Agregar imágenes de alta resolución muy grandes
- Uso de SVG/Canvas para dibujar escenas complejas
Estos son algunos ejemplos que se encuentran comúnmente en la Web:
- Un sitio SPA que reconstruye todo el DOM después de hacer clic en un vínculo, sin hacer una pausa para proporcionar una primera respuesta visual.
- Una página de búsqueda que ofrece filtros de búsqueda complejos con una interfaz de usuario dinámica, pero ejecuta objetos de escucha costosos para hacerlo.
- Un botón de activación del modo oscuro que activa el estilo o diseño de toda la página
10. Experimento: retraso en la presentación
requestAnimationFrame
tiene un funcionamiento lento
Simulemos una larga demora de presentación con la API de requestAnimationFrame()
.
Mueve la llamada a blockFor
a una devolución de llamada requestAnimationFrame
para que se ejecute después de que muestre el objeto de escucha de eventos:
Ver el código completo: presentación_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: presentación_delay.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
requestAnimationFrame(() => {
blockFor(1000);
});
});
La interacción dura un segundo, entonces, ¿qué pasó?
requestAnimationFrame
solicita una devolución de llamada antes de la siguiente pintura. Dado que INP mide el tiempo desde la interacción hasta la siguiente pintura, blockFor(1000)
en requestAnimationFrame
continúa bloqueando la siguiente pintura durante un segundo completo.
Sin embargo, ten en cuenta dos cosas:
- Cuando coloques el cursor sobre un elemento, verás todo el tiempo de interacción que se invierte en "demora en la presentación". ya que el bloqueo del subproceso principal se produce después de que vuelve 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. Cómo diagnosticar interacciones
En esta página de prueba, la capacidad de respuesta es muy visual, con las puntuaciones, los cronómetros y la IU de contador, pero cuando se prueba la página promedio, es más sutil.
Cuando las interacciones se prolongan, no siempre está claro cuál es la causa. Es:
- ¿Retraso en la entrada?
- ¿Duración del procesamiento de eventos?
- ¿Se retrasa la presentación?
En cualquier página que desees, puedes usar Herramientas para desarrolladores para medir la capacidad de respuesta. Para adoptar el hábito, prueba este flujo:
- Navega por la Web como lo harías normalmente.
- Opcional: Deja abierta la consola de Herramientas para desarrolladores mientras la extensión de Métricas web registra las interacciones.
- Si observas una interacción que tiene un rendimiento bajo, intenta repetirla:
- Si no puedes repetirlo, usa los registros de la consola para obtener estadísticas.
- Si puedes repetirlo, grábalo en el panel de rendimiento.
Todos los retrasos
Intenta agregar algunos de 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 cronómetros o solo actualizar el estado global, ¿qué sucede cuando estas actualizan la página finalmente?
Siempre y cuando se permita renderizar el siguiente procesamiento de imagen después de una interacción, incluso si el navegador decide que en realidad no necesita una nueva actualización de renderización, se detendrá la medición de interacciones.
Para probar esto, continúa actualizando la IU desde el objeto de escucha de clics, pero ejecuta la tarea de bloqueo desde el tiempo de espera.
Ver código completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
¿Qué sucede ahora?
14. Resultados del experimento de trabajo asíncrono
Ver código completo: timeout_100.html
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
setTimeout(() => {
blockFor(1000);
}, 100);
});
Ahora, la interacción es corta porque el subproceso principal está disponible inmediatamente después de que se actualiza la IU. La tarea de bloqueo prolongada aún se ejecuta, solo después de la pintura, por lo que el usuario obtendrá comentarios inmediatos de la IU.
Lección: si no puedes eliminarlo, al menos muévelo.
Métodos
¿Podemos hacer mejor que un 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 eliminado.
Objetivo:
- La interacción se ejecutará
incrementAndUpdateUI()
. blockFor()
se ejecutará lo antes posible, pero no bloqueará la siguiente pintura.- Esto da como resultado un comportamiento predecible sin “tiempos de espera mágicos”.
Estas son algunas formas de lograrlo:
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, hará que la interacción sea lenta), requestAnimationFrame
+ setTimeout
permiten un polyfill simple para requestPostAnimationFrame
, que ejecuta la devolución de llamada después de la siguiente pintura.
Ver código completo: raf+task.html
function afterNextPaint(callback) {
requestAnimationFrame(() => {
setTimeout(callback, 0);
});
}
button.addEventListener('click', () => {
score.incrementAndUpdateUI();
afterNextPaint(() => {
blockFor(1000);
});
});
En cuanto a la ergonomía, puedes incluirla en una promesa:
Ver 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 rápidos)
Mover el bloqueo largo puede ayudar, pero esas tareas largas siguen bloqueando la página y afectando a 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 si se te ocurre tu propia variación sobre el aplazamiento del trabajo en el último paso):
Ver 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, hay una tarea de un segundo en cola, lo que garantiza que el subproceso principal esté bloqueado durante un período considerable.
Cuando esas tareas largas se superponen con nuevos clics entrantes, se generan interacciones lentas, a pesar de que el objeto de escucha de eventos mismo devuelve resultados casi de inmediato. Creamos la misma situación que en el experimento anterior con retrasos en las entradas. Solo que esta vez, el retraso de entrada no proviene de un setInterval
, sino del trabajo activado por objetos de escucha de eventos anteriores.
Estrategias
Idealmente, queremos quitar las tareas largas por completo.
- Quita por completo el código innecesario, especialmente las secuencias de comandos.
- Optimiza el código para evitar ejecutar tareas largas.
- Anula el trabajo obsoleto cuando lleguen nuevas interacciones.
16. Estrategia 1: Anulación de rebote
Una estrategia clásica. Cuando las interacciones llegan en una sucesión rápida y los efectos de red o de procesamiento son costosos, retrasa el inicio del trabajo a propósito para que puedas cancelarlo y reiniciarlo. Este patrón es útil para interfaces de usuario como los campos de autocompletado.
- Usa
setTimeout
para retrasar el inicio de trabajos costosos, con un temporizador, aproximadamente entre 500 y 1,000 milisegundos. - Guarda el ID del temporizador cuando lo hagas.
- Si llega una nueva interacción, cancela el temporizador anterior con
clearTimeout
.
Ver 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 a que no haya ningún clic durante un segundo completo antes de ejecutarse. En el caso de las interacciones que se producen en aumentos de actividad, como escribir una entrada de texto o objetivos de elementos que se espera que obtengan varios clics rápidos, esta es una estrategia ideal para usar de forma predeterminada.
17. Estrategia 2: Interrumpir el trabajo de larga duración
Todavía existe la mala suerte de que recibas otro clic justo después de que haya transcurrido el período de espera, que se realice en medio de esa larga tarea y se convierta en una interacción muy lenta debido a la demora en las entradas.
Idealmente, si una interacción llega en medio de nuestra tarea, queremos pausar nuestro trabajo pesado para que cualquier interacción nueva se controle de inmediato. ¿Cómo podemos hacerlo?
Hay algunas APIs, como isInputPending
, pero en general, es mejor dividir las tareas largas en fragmentos.
Muchos setTimeout
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.
Volvimos a dedicar cinco segundos de trabajo a realizar cinco clics, pero cada tarea de un segundo por clic se dividió en diez tareas de 100 milisegundos. Como resultado, incluso con múltiples interacciones superpuestas con esas tareas, ninguna interacción tiene un retraso de entrada superior a 100 milisegundos. El navegador prioriza los objetos de escucha de eventos entrantes sobre el trabajo de setTimeout
, y las interacciones siguen siendo responsivas.
Esta estrategia funciona especialmente bien cuando se programan puntos de entrada separados, como cuando tienes un conjunto de funciones independientes a las que necesitas llamar en el tiempo de carga de la aplicación. El solo hecho de cargar las secuencias de comandos y ejecutar todo al momento de la evaluación de las secuencias de comandos puede ejecutar todo en una enorme tarea larga de forma predeterminada.
Sin embargo, esta estrategia no funciona tan bien para dividir el código con acoplamiento alto, como un bucle for
que usa el estado compartido.
Ahora con yield()
Sin embargo, podemos aprovechar async
y await
modernos para agregar "puntos de rendimiento" con facilidad. 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);
});
Al igual que antes, se produce el subproceso principal después de una parte del trabajo, y el navegador puede responder a cualquier interacción entrante, pero ahora todo lo que se necesita es un await schedulerDotYield()
en lugar de setTimeout
separados, lo que hace que sea lo suficientemente ergonómico para usarlo incluso en medio de un bucle for
.
Ahora con AbortContoller()
Eso funcionó, pero cada interacción programa más trabajo, incluso si han surgido nuevas interacciones y es posible que hayan cambiado el trabajo que se debe hacer.
Con la estrategia de rebote, cancelamos el tiempo de espera anterior con cada interacción nueva. ¿Podemos hacer algo similar aquí? Una forma de hacerlo es usar un AbortController()
:
Ver 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 ingresa un clic, se inicia el bucle for
de blockInPiecesYieldyAborty
que hace todo el trabajo necesario mientras procesa periódicamente el subproceso principal para que el navegador siga respondiendo a las interacciones nuevas.
Cuando ingresa 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 el primer bucle para volver a ejecutarse, nota que signal.aborted
ahora es true
y se muestra inmediatamente sin realizar más trabajo.
18. Conclusión
Separar todas las tareas largas permite que un sitio responda a las interacciones nuevas. Esto te permite proporcionar comentarios iniciales rápidamente y también te permite tomar decisiones como anular el trabajo en curso. A veces, eso significa programar los puntos de entrada como tareas independientes. A veces, esto significa agregar "rendimiento" puntos cuando sea conveniente.
Recuerda
- INP mide todas las interacciones.
- Cada interacción se mide desde la entrada hasta el siguiente procesamiento de imagen, 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 en la presentación todos afectan la capacidad de respuesta a la interacción.
- Puede medir fácilmente los desgloses de INP y de interacción con Herramientas para desarrolladores.
Estrategias
- No tengas código de larga duración (tareas largas) en tus páginas.
- Quita el código innecesario de los objetos de escucha de eventos hasta después de la siguiente pintura.
- Asegúrate de que la actualización de renderización sea eficiente para el navegador.