La introducción . A veces, sólo una función de JavaScript de larga duración es suficiente para congelar la interfaz, dejando a los usuarios frustrados e inseguros de si la aplicación sigue funcionando o se ha congelado. Se trata de un método pequeño pero poderoso: Permite a un usuario pausar la ejecución, dándole al navegador la oportunidad de manejar tareas más importantes (como hacer clic o escribir), y luego continuar donde se detuvieron. , explorar viejos workarounds, y ver cómo Facilita la vida. Planificación de tareas API Título de la campaña.yield() Main Thread scheduler.yield() ¿Cuál es la función de scheduler.yield()? Así, lo que es Es un método de la Interfaz de la nueva Este método le permite a usted, como desarrollador, pausar su ejecución de JavaScript y de forma explícita devolver el control a la - para que pueda manejar otras tareas importantes pendentes, como las interacciones de los usuarios, los clics, la escritura, etc., y luego continuar la ejecución desde donde se detuvo. Usted está diciendo al navegador: scheduler.yield() Calendario Planificación de tareas API. Main Thread scheduler.yield() "Espera, respira, paremos la tarea actual y nos concentramos en otras tareas no menos o más importantes.Una vez que hayas terminado, vuelva y continúa la ejecución desde donde nos dejamos". "Espera, respira, paremos la tarea actual y nos concentramos en otras tareas no menos o más importantes.Una vez que hayas terminado, vuelva y continúa la ejecución desde donde nos dejamos". Esto hace que su página sea más sensible, especialmente cuando se ejecutan tareas largas o pesadas de JavaScript. - que se refiere a la rapidez con la que el navegador responde a la entrada del usuario. Interacción con Next Paint (INP) La terminología. Antes de profundizar, vamos a pasar rápidamente por algunos términos básicos que se utilizarán a lo largo del artículo. Principal - Este es el lugar central donde el navegador hace la mayor parte de su trabajo. se ocupa de la renderización, el diseño y ejecuta la mayor parte de su código JavaScript. Tareas largas – Esta es cualquier tarea de JavaScript que mantiene ocupado el Thread Principal durante demasiado tiempo, generalmente más de 50 milisegundos. Tareas de bloqueo: Es una operación sincronizada en el hilo principal que impide que el navegador procesar otras cosas importantes, como responder a los clics o actualizar la interfaz de usuario. El problema . Para comprender la belleza de , primero tienes que entender qué problema está tratando de resolver. JavaScript se ejecuta en un solo hilo. Esto significa que solo puede hacer una cosa a la vez. Si tu código mantiene ocupado el hilo, todo lo demás – renderización, clics de botones, escritura de entrada tiene que esperar. En un mundo ideal, siempre dividirías tareas pesadas en piezas pequeñas. Pero la realidad es confusa. Se trata de código heredado, scripts de terceros, o cálculos pesados inevitables. Y cuando eso sucede, los usuarios se atrapan con páginas congeladas. scheduler.yield() Modelo de ejecución de JavaScript. Como un rápido refresco, aquí está un diagrama de ejemplo de cómo procesan las tareas JavaScript – en otras palabras, cómo el Estoy seguro de que muchos de ustedes han visto diagramas como este antes – las filas de tareas, el ciclo de eventos, la pila de llamadas. JavaScript Execution Model Pasemos a través de las principales ideas paso a paso: Todo el código sincronizado va directamente a la pila de llamadas y se ejecuta línea por línea, función por función. Sigue el principio LIFO - último en, primero fuera. JavaScript se ejecuta en un solo hilo, lo que significa que sólo puede hacer una cosa a la vez. Las operaciones asíncronas (como setTimeout, fetch) se manejan fuera del Thread Principal – por las APIs Web (proporcionadas por el navegador o el entorno). Una vez que se completan, no vuelven directamente a la pila de llamadas. En cambio, sus llamadas son en cola – ya sea en la cola de microtasks (por ejemplo, Promise.then, colaMicrotask) o en la cola de macrotasks (por ejemplo, setTimeout, setInterval). Cuando el Stack de llamadas está vacío, el Loop de Eventos comprueba la cola de microtascas y ejecuta todas las microtascas una por una en orden. Sólo después de eso, toma una macrotask de la cola y la ejecuta. Si, durante el proceso, se añaden nuevas microtascas, se ejecutan antes de la siguiente macrotasca. Este ciclo continúa: todas las microtascas → una macrotasca → se repiten. El nuevo código sincronizado se ingresa a la pila de llamadas cuando llegan nuevas tareas, como cuando un usuario hace clic en un botón, se ejecuta un nuevo script o cuando una microtasca o macrotasca ejecuta su llamada. Esta es una explicación muy breve y superficial, solo para recordarle cómo funciona. Descripción del problema. Ahora que has renovado tu comprensión de cómo JavaScript ejecuta tareas, echemos un vistazo más de cerca al problema real que viene con este modelo. El problema es simple: cuando una tarea toma demasiado tiempo en el hilo principal, bloquea todo lo demás – interacciones de usuario, renderización de actualizaciones y animaciones. Esto conduce a la congelación de la interfaz de usuario y la mala respuesta. El primer pensamiento obvio podría ser: "Bueno, simplemente no escribas funciones largas o pesadas, y eso es lo que es. El problema está resuelto". Y sí, eso es cierto - en un mundo ideal, siempre dividirías el código pesado en partes más pequeñas, optimizarías todo, y evitarías bloquear el hilo principal. Pero seamos honestos - muchos de nosotros hemos entrado en estos problemas, incluso si no éramos los que los causamos originalmente. Para ello, crearemos una función llamada que actúa como una tarea de bloqueo para el hilo principal durante el período de tiempo especificado.La función simula este tipo de cálculo "pesado" en cada elemento de la matriz. blockingTask() Desafortunadamente, los bloques de código no muestran números de líneas.En mis explicaciones, a veces me refiero a líneas específicas (por ejemplo, "la línea 5 hace X"). Desafortunadamente, los bloques de código no muestran números de líneas.En mis explicaciones, a veces me refiero a líneas específicas (por ejemplo, "la línea 5 hace X"). function blockingTask(ms = 10) { const arr = []; const start = performance.now(); while (performance.now() - start < ms) { // Perform pointless computation to block the CPU. arr.unshift(Math.sqrt(Math.random())); } return arr; } No hay nada fantástico sobre la función, aquí está todo lo que hace: Acepta un argumento - el número de milisegundos. Este es el tiempo mínimo que la función se ejecutará, ocupando así el hilo principal. Crea un array vacío. Crea un tiempo de inicio (como un tiempo actual). Luego corre un lapso de tiempo hasta que haya pasado el tiempo especificado. Dentro del ciclo, sólo hace cálculos aleatorios y sin sentido para simular la carga. Finalmente, devuelve el resultado de los cálculos. La función no hace nada útil, pero simula un escenario del mundo real de carga pesada. Imagínese una situación común en la que necesita fluir a través de un conjunto de datos y aplicar ese trabajo pesado a cada elemento. Para ello, vamos a crear un Funciones : heavyWork() function heavyWork () { const data = Array.from({ length: 200 }, (_, i) => i) const result = [] for (let i = 0; i < data.length; i++) { result.push(blockingTask(10)) } return result; } En el que sucede lo siguiente: En la línea 2, crea un conjunto de 200 elementos, sólo números de 0 a 199. Quiero señalar que 200 elementos no son tantos, pero será suficiente para ver la esencia del problema. Luego, se crea un nuevo conjunto de "resultados" vacío para almacenar los valores procesados. La línea 5 declara un ciclo que pasa por toda la longitud de la matriz de datos. Dentro del loop, ejecutamos la función BlockingTask() simulando 10 milisegundos de trabajo para cada elemento, y el resultado se añade a la matriz "resultado". Una vez más, quiero recordarle que, para la demostración, la función BlockingTask() no lleva ninguna carga semántica. Simplemente realiza algún trabajo imaginario, intensivo en recursos. En el mundo real, podría ser algún procesamiento intensivo en el trabajo de un elemento de la matriz. Finalmente, devuelve el array resultante. Sólo 10 milisegundos por elemento, y solo 200 elementos – pero juntos, bloquean el hilo principal durante 2 segundos completos. La demostración del problema. Ahora es el momento de mirar el problema no sólo en teoría, sino en acción.Esta no es una demostración completa todavía - piense en ello como una visual simplificada para ayudarle a ver claramente el problema. Aquí está lo que ves: La ventana izquierda, titulada "Configuración", le permite activar y desactivar el bloqueo de hilo principal - lo que significa si la función de bloqueoTask() está realmente en ejecución. También puede cambiar la funcionalidad scheduler.yield() - llegaremos a esa parte más adelante. La ventana titulada "Heavy Task" ejecuta la función heavyWork(). Esta es la que procesa una matriz usando blockingTask() en cada elemento si el bloqueo de hilo principal está habilitado. Y la ventana titulada "Logger" solo registra el tiempo actual a la consola, incluyendo milisegundos. Veamos lo que sucede cuando el El bloqueo está desactivado, por lo que las tareas son muy ligeras.Es sólo un loop sobre un conjunto de 200 elementos, sin ningún cálculo complejo. Main Thread Lo que observas: El usuario hace clic en el botón "OK" - la función heavyWork() se ejecuta, y inmediatamente se devuelve. Esto se indica por el mensaje HEAVY_TASK_DONE en la consola, seguido del resultado - una matriz de números. Luego, el usuario hace clic en el botón "Log" tres veces, para registrar la hora actual en la consola - los timestamps aparecen de inmediato, con una ligera diferencia de tiempo. El usuario ejecuta la función heavyWork() de nuevo, y de nuevo, respuesta instantánea. Finalmente, el usuario cierra dos ventanas, que en realidad simplemente elimina esos elementos del DOM. En este caso, todo se siente rápido y responsivo.El navegador no tiene problemas para manejar las interacciones, porque el hilo principal permanece libre. Ahora, permitamos que el bloqueo, de modo que para cada elemento de la matriz, el La función se llamaría con un retraso de sólo 10 milisegundos. Main Thread blockingTask() Y ahora puedes observar que la interacción del usuario con los elementos de la interfaz de usuario se ha vuelto menos suave, han aparecido congelaciones de la interfaz de usuario. El usuario presiona el botón "OK", lanzando así la función heavyWork() y el primer atraso que ocurre es que el botón "OK" permanece visualmente presionado. ¿Por qué? Porque el navegador no puede repaint mientras heavyWork() todavía bloquea el hilo principal. Y es importante entender que estamos hablando no solo de la tarea actual, sino de la pila de llamadas en su conjunto. Durante este tiempo, el usuario hace clic en el botón "Log" cuatro veces - nada sucede. Los clics se registran y sus manipuladores se añaden a la cola, pero el navegador no puede reaccionar. Sólo después de que heavyWork() termine, se ve la salida de la consola: primero el resultado heavyWork(), luego los cuatro timestamps - todos impresos en un lote. Y sólo después de eso, el botón "OK" cambió su estado y se volvió sin presionar. Luego, el usuario hace clic en el botón "OK" de nuevo. El mismo comportamiento - botón de pegada. Luego, mientras la tarea heavyWork() está en ejecución, intenta cerrar una ventana haciendo clic en el icono "X" tres veces. Y por último, otro intento de ejecutar heavyWork() y cerrar la última ventana. Esta demostración simple muestra cuánto tiempo las tareas bloquean la capacidad del navegador de responder a las acciones de los usuarios. Aunque cada llamada de bloqueo tarda solo 10 milisegundos, la cadena de 200 de ellos juntos resulta en un congelamiento de 2 segundos. El usuario no puede interactuar con los botones, la interfaz no se vuelve a pintar. Los eventos se encuentran en cola, pero no se procesan hasta que la pila de llamadas sea clara. Esto no es solo un problema de rendimiento, es un problema de experiencia del usuario. Y eso es exactamente el tipo de problema que queremos resolver – idealmente, sin tener que dividir manualmente nuestra lógica en docenas de llamadas. La solución del problema. Ahora que entiende el problema, hablemos de posibles soluciones.Por supuesto, la mejor estrategia es evitar tareas largas en primer lugar manteniendo el código eficiente y rompiendo las cosas temprano.Pero, como ha visto, las cosas suceden. Ya sea que se trate de código heredado, cálculos inevitables, o simplemente no hay suficiente tiempo para optimizar, a veces, tienes que lidiar con ello. aparecieron, se han creado varias soluciones y trucos para mejorar la capacidad de respuesta. pero la idea central detrás de todos ellos - y detrás También - es bastante simple: Prioritized Task Scheduling API scheduler.yield() Divide una tarea en piezas más pequeñas o llamadas pedazos. Y de vez en cuando, pausa para dejar que el navegador tome su respiración. En otras palabras, le da al hilo principal la oportunidad de ejecutar tareas más urgentes, como interacciones con los usuarios o renderizar actualizaciones, y luego vuelve a terminar su propio trabajo. Aquí está el concepto de La función se ve como en pseudocódigo: heavyWork() function heavyWork() { // Do heavy work... /** * Take a breather! * Yield the execution to the Main Thread... * */ // Continue to do heavy work... } ¿Qué está sucediendo aquí: Estás realizando una parte de tu tarea. Luego, se hace una pausa, permitiendo al navegador manejar otras tareas de alta prioridad (como las actualizaciones de la interfaz de usuario). Siga ejecutando la función desde donde se detuvo. Viejos enfoques de resolución de problemas. Antes El truco más común para lidiar con tareas de bloqueo largas era usar Al llamarlo con un 0 (zero) retraso, se agrega su tarea de llamadas al final del la cola, permitiendo que otras tareas se ejecuten primero. En otras palabras, le dices al navegador: scheduler.yield() setTimeout() macrotasks "Executa este poco de código más tarde, después de que hayas manejado todo lo demás". "Executa este poco de código más tarde, después de que hayas manejado todo lo demás". Así es como puedes darle al hilo principal una breve respiración entre los trozos de trabajo pesado. La función puede parecer usar este enfoque: heavyWork() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise(resolve => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Vamos a romper lo que está sucediendo aquí: Línea 3: Se crea una promesa y su ejecutor se ejecuta de inmediato, programando una setTimeout() con cero retraso. El llamado del timeout (que resuelve la promesa) se añade al final de la cola de macrotask. Debido a la espera, el resto de la función async se detiene. Técnicamente, esta continuación se añade a la cola de microtask, esperando que la promesa resuelva. El motor JavaScript comprueba el Stack de llamadas – una vez que está vacío, el Loop de Eventos se inicia. Primero, mira la cola de microtask – pero dado que la promesa no está resuelta todavía, no hay nada que ejecutar. Luego, el Loop de Eventos selecciona la macrotask de la cola (en nuestro ejemplo, es el Time setout Línea 9: Calculamos cuántas veces queremos rendir al Thread Principal, aproximadamente cada 25% del trabajo. Este número puede variar dependiendo de lo pesado que sea la tarea. Líneas 13-15: Dentro del loop, si se cumple la condición para el intervalo de rendimiento, la ejecución se transfiere al hilo principal, es decir, la técnica setTimeout() se repite, permitiendo que el proceso del navegador interactúe con el usuario o redrive la interfaz. En esencia, este enfoque funciona – es relativamente simple y mejora la capacidad de respuesta. Pero hay compromisos. No está construido para una planificación precisa.Pone tareas al final de la cola de las macrotascas, y cualquier cosa que ya esté en esa cola puede retrasar su continuación. setTimeout() Por ejemplo, digamos que otra parte de la página utiliza Realizar tareas de forma regular: setInterval() setInterval(() => { /* Another heavy work... */ }) async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await new Promise(resolve => setTimeout(resolve, 0)) const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await new Promise((resolve, reject) => setTimeout(resolve, 0)) } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Ahora su propia tarea - el siguiente trozo de función – puede retrasarse por uno o más de estos llamados de intervalo. El navegador sólo ejecuta lo que está a continuación en la fila, y no controla el orden. Una forma de permitirle rendir, no sabe exactamente cuándo recuperará el control. heavyWork() setTimeout() Hay otras maneras de abordar la situación. función, que le permite programar el trabajo justo antes de la próxima repaint. , y tiene desventajas similares. o que ejecuta tu código durante un tiempo ininterrumpido del navegador. No es una alternativa, pero bueno para el fondo, trabajo menos importante, que ayuda al hilo principal a ser libre para tareas más críticas. En general, podríamos discutir otras estrategias para resolver y prevenir tales problemas. Sin embargo, para permanecer en el tema, vamos a seguir adelante y ver lo que trae a la mesa. requestAnimationFrame() setTimeout() requestIdleCallback() scheduler.yield() Página de inicio.ayuda( - es una forma moderna de pausar la ejecución, y rendir el control al hilo principal, lo que permite al navegador realizar cualquier trabajo de alta prioridad pendiente, y luego continuar la ejecución desde donde se detuvo. la expresión se alcanza, la ejecución de la función actual en la que se llamó se suspende, y da control al hilo principal, rompiendo así, o parando, la tarea actual.La continuación de la función, es decir, la ejecución de la parte restante de ella, desde donde se dejó, es una microtarea separada, recién programada en el ciclo de eventos. scheduler.yield() await scheduler.yield() La belleza de Entonces, la continuación después de permanece en la parte delantera de la cola, y está programado para correr cualesquiera otras tareas no esenciales que hayan sido colgadas.La principal diferencia de la Este enfoque es con , estas continuas normalmente se ejecutan después de cualquier nueva tarea que ya haya estado en cola, lo que puede causar largos retrasos entre el rendimiento al hilo principal y su finalización. scheduler.yield() scheduler.yield() BEFORE setTimeout() setTimeout() El siguiente diagrama ilustra cómo los tres enfoques se comparan en la práctica: In the first example, without yielding to the main thread: At first, the long " " runs uninterrupted, blocking the main thread and UI accordingly. Then, a user event is processed – a button click triggered during the execution of " ". And finally, " " is executed – callback scheduled earlier or during the execution of the long task. Task 1 Task 1 Task 2 setTimeout() In the second example, using as a yielding to the main thread: The execution queue is different. At first, the long " " runs. Then, when the yield to the main thread happens, " " pauses to let the browser breathe, and the button click is processed. But after the button click is processed, the callback will be executed first, which could have been scheduled in advance or during the execution of " ". And finally, only after that, the continuation of " " will be executed. setTimeout() Task 1 Task 1 setTimeout() Task 1 Task 1 In the last example, using : After the long " " has been paused and the user click event has been processed, then the continuation of " " is prioritized and runs before any queued tasks. scheduler.yield() Task 1 Task 1 setTimeout() In summary, is a more intelligent and predictable way to give the main thread breathing room. It avoids the risk of your code being pushed too far back in the queue and helps maintain performance and responsiveness, especially in complex applications. scheduler.yield() las prioridades. Así que, ¿qué causa tal diferencia en el comportamiento? Todo se trata de prioridades! Como desarrolladores, generalmente no pensamos en el orden de ejecución de tareas en el ciclo de eventos en términos de prioridades. Más precisamente, tenemos una buena comprensión de lo que y Pero si miras más profundamente, notarás que también hay prioridades implícitas en juego.Por ejemplo, un manipulador de botones, disparado por la acción del usuario, generalmente ejecutará antes de que un El reencuentro, aunque ambos Como se mencionó anteriormente, Es parte de la – una interfaz extensa y rica en características que merece su propia discusión completa separada y está claramente más allá del alcance de esta conversación. Sin embargo, es importante mencionar una de sus características clave: la introducción de un modelo de prioridad de tareas claro. API de planificación de tareas priorizadas simplemente hace que estas prioridades sean explícitas, lo que facilita determinar qué tarea se ejecutará primero, y permite ajustar las prioridades para cambiar el orden de ejecución, si es necesario. microtasks macrotasks setTimeout() macrotasks scheduler.yield() Prioritized Task Scheduling API " " – The highest priority tasks that directly affect user interaction, such as handling clicks, taps, and critical UI operations. user-blocking "Visible para el usuario" - Las tareas que afectan la visibilidad de la interfaz de usuario o el contenido, pero no son críticas para la entrada inmediata. Tareas que no son urgentes y que pueden posponerse de forma segura sin afectar a la experiencia del usuario actual, y que no son visibles para el usuario. por defecto, Tiene un “ “Prioridad” también Exponen el método, designado para agendar tareas con una prioridad especificada de lo anterior.Aunque no va a entrar en detalles sobre este método aquí, vale la pena mencionar que si was scheduled from within a heredó su prioridad. scheduler.yield() user-visible Prioritized Task Scheduling API postTask() scheduler.yield() postTask() Cómo usar scheduler.yield(). Una vez que entienda cómo funciona todo: los tipos de tareas, el problema causado por las operaciones de bloqueo largas y las prioridades, el uso de Pero debe usarse sabiamente y con la debida precaución.Aquí está una versión actualizada del Funciones Usando Ahora, en lugar de Sólo tienes que llamar Y el resto se mantiene sin cambios. scheduler.yield() heavyWork() scheduler.yield() setTimeout() await scheduler.yield() async function heavyWork() { // Yield to Main Thread to avoid UI blocking before heavy work await scheduler.yield() const data = Array.from({ length: 200 }, (_, i) => i) const result = [] // Interval at which execution will be yielded to the main thread (approx. ~ 25%). const yieldInterval = Math.ceil(data.length / 4) for (let i = 0; i < data.length; i++) { // Yield control to Main Thread to update UI and handle other tasks. if (i % yieldInterval === 0) { await scheduler.yield() } result.push(threadBlockingEnabled ? blockingTask(10) : data[i]) } return result } Cuando un usuario comienza a Funciones Usando la diferencia es inmediatamente notable. en primer lugar, el " " botón no se pega, y en segundo lugar, el usuario hace clic en eventos en el " los botones se procesan con éxito, lo que no bloquea la interacción del usuario con la página. heavyWork() scheduler.yield() OK Log Es decir, en primer lugar, el la función se lanzó, y el botón se re-rendió sin pegar. Mientras esta tarea pesada se estaba ejecutando, el usuario presionó el " El evento se procesó con éxito y los datos se imprimieron en la consola. la función continuó, y su resultado final fue impreso en la consola. Después de la finalización, el usuario presionó la En resumen, puede darle a su navegador una pausa con sólo una línea. heavyWork() Log heavyWork() Log La demo. Descripción de la funcionalidad. Ahora que has explorado la teoría, pasemos a la práctica y miremos una demostración de trabajo real.Esta es una aplicación bancaria simulada.Por supuesto, es ficticia y simplificada, pero captura lo suficiente de la complejidad del mundo real para ayudarle a entender cómo bloquear el hilo principal afecta a la interactividad, y cómo puede ayudar. scheduler.yield() Aquí está lo que el usuario ve en la interfaz: – By default, the account balance is hidden behind a placeholder of asterisks. This is a familiar pattern in real banking apps, where sensitive information is hidden unless explicitly revealed by the user. A button labeled " " toggles visibility. Balance section Show balance – A visual representation of a bank card, shown front side by default, where some details are displayed: card type in the top left corner, last 4 digits of the card, the cardholder's name, and payment system, at the bottom right corner of the card. There are two buttons to the right of the card: Bank card – which flips the card when clicked. The back side of the card reveals sensitive card data like its full number, expiration date, and CVV code. Although the card number is generally not considered private information, some applications still prefer not to show the full number by default, but only if the user initiates it. However, I know and even use banks that generally do not allow you to see the bank card number in the application. Show card details – by clicking this button, this feature supposedly generates a list of transactions on the card and displays them in the table below. It imitates the real functionality where users can generate reports on bank card transactions. In reality, these reports can be complex tables with many customizable filters and the ability to download the report as a file. Such operations might involve heavy computations, process a huge amount of data, making them resource-intensive and time-consuming. For the sake of the demo, it's simplified. Under the hood, the " " button triggers the previously discussed function, which simply blocks the main thread using the function, which was also discussed above. After that, static mock transaction data is simply rendered into the table. Generate report Generate report heavyWork() blockingTask() El comportamiento de la aplicación puede ser personalizado utilizando los diferentes ajustes en el panel de configuración en el lado izquierdo. Ahora es el momento de explicar lo que hace: Bloqueo de Thread Principal – determina si el Thread Principal será bloqueado. De hecho, cuando esta opción está habilitada, se ejecuta la función de bloqueoTask(). Scheduler.yield() – Regula si se utiliza scheduler.yield() Longitud de array de datos – Controla cuántos elementos son iterados por la función heavyWork(). Duración del tiempo de bloqueo: Especifica cuántos milisegundos toma cada elemento de la matriz para procesar. Intervalo de rendimiento – Define cuántas veces se llama scheduler.yield() como porcentaje de progreso a través de la matriz. Es decir, cuanto más bajo sea este número, más a menudo será llamado. En ejemplos anteriores, usamos una matriz de 200 elementos con un retraso de 10 ms y un intervalo de 25% – un buen equilibrio para un impacto visible sin retraso excesivo. Con conjuntos de datos más grandes, un intervalo más pequeño a menudo es mejor. Pero, como siempre, depende. La demostración . Después de haber ordenado toda la funcionalidad y configuración, pasemos por un escenario de uso real y veremos cómo bloquear el hilo principal afecta a la experiencia del usuario. Bloquear y deshabilitar También aumentaremos un poco la longitud de la matriz, por lo que la operación pesada toma más tiempo, dándonos tiempo para observar los efectos. detrás de las escenas, esto desencadena la Función, que procesa 1000 elementos, donde cada elemento toma 10 milisegundos. Main Thread scheduler.yield() Generate report heavyWork() Mira lo que pasa: “La " botón permanece atrapado, no se desprende, y la interfaz de usuario no vuelve a renderizar. Mientras el informe se está generando, el usuario intenta hacer clic en " “Entonces” " botones, pero nada responde. La interfaz está completamente congelada, no hay animación, no hay feedback, no hay sensación de progreso. Este es un ejemplo clásico de una mala experiencia del usuario. La aplicación aparece congelada, aunque técnicamente todavía está funcionando. Generate report Show card details Show balance Vamos a abordar estas deficiencias utilizando A continuación te mostramos cómo se ve la configuración: todavía está bloqueado. Esta vez, la opción de usar La longitud de la matriz se incrementa ligeramente, sólo para la claridad. El tiempo de bloqueo permanece el mismo, 10 milisegundos. El intervalo de respuesta se reduce al 5% para una respuesta más suave, ya que se ha aumentado la longitud de la matriz. scheduler.yield() Main Thread scheduler.yield() scheduler.yield() Y ahora con la configuración actualizada, el mismo flujo de usuario se ve completamente diferente.La primera cosa que llama la atención es que después del " " botón ha sido clicado, se vuelve a renderizar correctamente, y aparece la animación de carga. Mientras se genera el informe, el usuario interactúa con éxito con la interfaz de usuario: pueden girar la tarjeta y cambiar el balance. La aplicación sigue siendo responsiva, incluso si las animaciones son ligeramente menos suaves, es un gran paso adelante en comparación con el congelamiento anterior. Esta es una experiencia mucho mejor. El usuario está informado, en control, y no se deja adivinar si la aplicación está funcionando. Y todo lo que tomó fue... una llamada de método: Por supuesto, la implementación real se puede optimizar aún más, pero incluso en esta forma simple, la diferencia es notable. Generate report scheduler.yield() La conclusión . Así que hoy has aprendido acerca de darle un descanso a tu navegador, la importancia de rendir para realizar tareas de mayor prioridad, y las ventajas y desventajas de estas técnicas. tiene otras capacidades que no estaban cubiertas en este artículo. pero mi objetivo era darle una base sólida, suficiente para comenzar a experimentar, y suficiente para empezar a pensar de manera diferente sobre cómo su código juega con el navegador. Gracias por leer, y darle a su navegador una pausa de vez en cuando! Main Thread Prioritized Task Scheduling API Un enlace útil. Demo en línea – https://let-your-browser-take-a-breather.onrender.com/ Demo repositorio de GitHub – https://github.com/WOLFRIEND/let_your_browser_take_a_breather Diagrama – https://drive.google.com/file/d/1FLKKPaseyypE3pVXXn7Cj0aWac3rCayn/view