¿Cómo se realiza las tareas macro de procesamiento de bucle de eventos del navegador? -- javascript campo con google-chrome campo con firefox campo con browser campo con event-loop camp Relacionados El problema

How is browser event loop processing macro tasks?


0
vote

problema

Español

Vi a Jake Archibald's Talk sobre Event Loop - https://vimeo.com/254947206 . Sobre la base de la conversación, mi entendimiento fue que el bucle de eventos ejecutará tantas tareas macro, ya que puede caber en un marco y si hay alguna tarea de macro de larga duración, hará que los marcos se salten. Por lo tanto, mi expectativa fue que cualquier tarea, corriendo más tiempo, entonces la duración habitual del marco, hará que otras tareas se ejecuten en el siguiente cuadro. Probé eso al crear un botón y varios manipuladores como este https://codepen.io/jbojcic1/full/ qlggvw

Me di cuenta de que, aunque Handlerone se está ejecutando (debido al calculador de Fibonacci intensivo computacional), los manipuladores 2, 3 y 4 aún se ejecutan en el mismo marco. Solo TimeHandler está siendo ejecutado en el siguiente marco. Aquí están los registros que recibo:

    animationFrameCallback - 10:4:35:226   handler one called. fib(40) = 102334155   handler two called.   handler three called.   handler four called.   animationFrameCallback - 10:4:36:37   timeout handler called   animationFrameCallback - 10:4:36:42   

Entonces, la pregunta es ¿por qué los manejadores son dos, tres y cuatro ejecutados dentro del mismo marco que el manejador?

Para hacer las cosas aún más confusas de acuerdo con https: // desarrollador .mozilla.org / es-eS-US / DOCS / WEB / API / Frame_Timing_API ,

Un marco representa la cantidad de trabajo que hace un navegador en un evento iteración de bucle tal como procesamiento de eventos DOM, cambio de tamaño, desplazamiento, Representación, Animaciones CSS, etc.

y para explicar "una iteración de bucle de eventos" que vincularon https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-8 donde se indica que en una iteración:

  • se procesa una tarea macro,
  • todas las tareas micro se procesan
  • se actualiza
  • ... (también hay algunos otros pasos que son No es importante para esto)

que no parece ser correcto en absoluto.

Original en ingles

I watched Jake Archibald's talk about event loop - https://vimeo.com/254947206. Based on the talk my understanding was that event loop will execute as many macro tasks as it can fit in one frame and if there is some long running macro task it will cause frames to be skipped. So my expectation was that any task, running longer then usual frame duration, would cause other tasks to be executed in the next frame. I tested that by creating one button and multiple handlers like this https://codepen.io/jbojcic1/full/qLggVW

I noticed that even though handlerOne is long running (due to calculating computationally intensive fibonacci), handlers 2, 3 and 4 are still executed in the same frame. Only timeoutHandler is being executed in the next frame. Here are the logs I am getting:

  animationFrameCallback - 10:4:35:226   handler one called. fib(40) = 102334155   handler two called.   handler three called.   handler four called.   animationFrameCallback - 10:4:36:37   timeout handler called   animationFrameCallback - 10:4:36:42 

so the question is why are handlers two, three and four executed within the same frame as handler one?

To make things even more confusing according to https://developer.mozilla.org/en-US/docs/Web/API/Frame_Timing_API,

A frame represents the amount of work a browser does in one event loop iteration such as processing DOM events, resizing, scrolling, rendering, CSS animations, etc.

and to explain "one event loop iteration" they linked https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-8 where it's stated that in one iteration:

  • one macro task is processed,
  • all micro tasks are processed
  • rendering is updated
  • ... (there are some other steps too which are not important for this)

which doesn't seem to be correct at all.

              

Lista de respuestas

3
 
vote

Estás mezclando algunos conceptos aquí.

El "Frame" que está midiendo en su Codepen es el de la Paso 10 - Actualice la representación . Citando las especificaciones:

Esta especificación no exige ningún modelo en particular para seleccionar oportunidades de representación. Pero, por ejemplo, si el navegador está intentando lograr una frecuencia de actualización de 60Hz, entonces se producen oportunidades de renderizado en un máximo de cada 60 de un segundo (aproximadamente 16.7ms). Si el navegador encuentra que un contexto de navegación no puede sostener esta tasa, podría caer a las 30 oportunidades de representación más sostenibles por segundo para ese contexto de navegación, en lugar de caer en ocasiones. De manera similar, si un contexto de navegación no está visible, el agente de usuario puede decidir reducir esa página a las oportunidades de renderización 4 por segundo, o incluso menos.

Por lo tanto, no está seguro de que la frecuencia se incendiará este " marco ", pero en general es de 60 fps (la mayoría de los monitores se actualizan a 60Hz), por lo que en este lapso del tiempo, a Lot de iteraciones de bucles de eventos normalmente ocurrirán.

Ahora, SoleganImationFrame es aún más especial porque puede descartar Frames Si el navegador piensa que tiene demasiadas cosas para realizar. Por lo tanto, su FibonAcci probablemente retrasará cualquier ejecución de devoluciones de llamada RAF hasta que haya terminado.


En cuanto al artículo MDN en el que vinculó las conversaciones es un " marco " en el ámbito del ActuaciónFramaFrameTiming API . Debo admitir directamente que no tengo mucho conocimiento sobre esta API en particular, y dada su muy limitado apoyo del navegador, no creo que debamos pasar demasiado tiempo, excepto para decir que esto no tiene nada que ver. con un marco de pintura.

Creo que la herramienta más precisa que tenemos actualmente para medir una iteración de eventos es la API de mensajería .
Al crear un bucle de eventos de mensajes de autocontrol, podemos enganchar a todas las iteraciones de eventos.

  let stopped = false; let eventloops = 0; onmessage = e => {   if(stopped) {     console.log(`There has been ${eventloops} Event Loops in one anim frame`);     return;   }   eventloops++   postMessage('', '*'); }; requestAnimationFrame(()=> {   // start the message loop   postMessage('', '*');   // stop in one anim frame   requestAnimationFrame(()=> stopped = true); });  

Veamos cómo se comporta su código a nivel más profundo:

  let done = false; let started = false; onmessage = e => {   if (started) {     let a = new Date();     console.log(`new EventLoop - ${a.getHours()}:${a.getMinutes()}:${a.getSeconds()}:${a.getMilliseconds()}`);   }   if (done) return;   postMessage('*', '*'); }  document.getElementById("button").addEventListener("click", handlerOne); document.getElementById("button").addEventListener("click", handlerTwo); document.getElementById("button").addEventListener("click", handlerThree); document.getElementById("button").addEventListener("click", handlerFour);  function handlerOne() {   started = true;   setTimeout(timeoutHandler);   console.log("handler one called. fib(40) = " + fib(40)); }  function handlerTwo() {   console.log("handler two called."); }  function handlerThree() {   console.log("handler three called."); }  function handlerFour() {   console.log("handler four called.");   done = true; }  function timeoutHandler() {   console.log("timeout handler called"); }  function fib(x) {   if (x === 1 || x === 2) return 1   return fib(x - 1) + fib(x - 2); } postMessage('*', '*');  
  <button id="button">Click me</button>  

OK, por lo que en realidad hay un cuadro de un cuadro como en Eventloop iteración para disparar entre los manipuladores de eventos y la devolución de llamada de SettimeOut. Me gusta mejor.

Pero, ¿qué pasa con eso "marcos de larga duración" ¡Nos enteramos de?

Supongo que está hablando de la "Spin the event Loop" algoritmo, que está destinado a permitir que el ciclo de eventos no bloquee toda la interfaz de usuario en algunas circunstancias.

Primero, las especificaciones solo le indican a los implementadores que es una recomendación para ingresar a este algoritmo para los scripts de larga duración, no es una necesidad.

Luego, este algoritmo es permitir el procesamiento normal de eventos de eventos de registro de eventos y actualizaciones de la UI, pero cualquier cosa relacionada con JavaScript se reanuda simplemente en la próxima iteración de eventos.

Así que en realidad no hay forma de JS para saber si entramos en este algoritmo.

Incluso mi bucle impulsado por Messageevent no puede decirlo, porque el manejador de eventos se presionará después de que salgan de este script de larga duración.

Aquí hay un intento de poner de manera más gráfica, a riesgo de ser técnicamente inexacto:

  /**  * ...  * - handle events  *    user-click => push([cb1, cb2, cb3]) to call stack (* - paint if needed (may execute rAF callbacks if any))  *  * END OF LOOP —————————————————————————  * BEGIN OF LOOP  *  * - execute call stack  *    cb1()  *      schedule `timeoutHandler`  *      fib()  *      ...  *      ...  *      ...  *      ... <-- takes too long => "spin the event loop"  * [ pause call stack ]  * - handle events (* - paint if needed (but do not execute rAF callbacks))  *  * END OF LOOP —————————————————————————  * BEGIN OF LOOP  *  * - execute call stack  * [ resume call stack ]  *      (*fib()*)  *      ...  *      ...  *    cb2()  *    cb3()  * - handle events  *   `timeoutHandler` timed out => push to call stack (* - paint if needed (may execute rAF callbacks if any) )  *  * END OF LOOP —————————————————————————  * BEGIN OF LOOP  *  * - execute call stack  *   `timeoutHandler`()  * - handle events  ...  */     
 

You are mixing a few concepts here.

The "frame" you are measuring in your codepen is the one of the step 10 - Update the rendering. Quoting the specs:

This specification does not mandate any particular model for selecting rendering opportunities. But for example, if the browser is attempting to achieve a 60Hz refresh rate, then rendering opportunities occur at a maximum of every 60th of a second (about 16.7ms). If the browser finds that a browsing context is not able to sustain this rate, it might drop to a more sustainable 30 rendering opportunities per second for that browsing context, rather than occasionally dropping frames. Similarly, if a browsing context is not visible, the user agent might decide to drop that page to a much slower 4 rendering opportunities per second, or even less.

So it is not sure at which frequency will this "frame" fire, but generally it is at 60FPS (most monitors refresh at 60Hz), so in this lapse of time, a lot of event loops iterations will normally occur.

Now, requestAnimationFrame is even more special in that it can discard frames if the browser thinks it has too much things to perform. So your fibonacci will most probably delay any execution of rAF callbacks until it's done.


What the MDN article you linked talks about is a "frame" in the realm of the PerformanceFrameTiming API. I must admit directly that I don't have a lot of knowledge about this particular API, and given its very limited browser support, I don't think we should spend too much time on it, except to say that this has nothing to do with a painting frame.

I think the most precise tool we have currently for measuring an EventLoop iteration is the Messaging API.
By creating a self-calling message event loop, we can hook to every EventLoop iterations.

let stopped = false; let eventloops = 0; onmessage = e => {   if(stopped) {     console.log(`There has been ${eventloops} Event Loops in one anim frame`);     return;   }   eventloops++   postMessage('', '*'); }; requestAnimationFrame(()=> {   // start the message loop   postMessage('', '*');   // stop in one anim frame   requestAnimationFrame(()=> stopped = true); });

Let's see how your code behaves at a deeper level:

let done = false; let started = false; onmessage = e => {   if (started) {     let a = new Date();     console.log(`new EventLoop - ${a.getHours()}:${a.getMinutes()}:${a.getSeconds()}:${a.getMilliseconds()}`);   }   if (done) return;   postMessage('*', '*'); }  document.getElementById("button").addEventListener("click", handlerOne); document.getElementById("button").addEventListener("click", handlerTwo); document.getElementById("button").addEventListener("click", handlerThree); document.getElementById("button").addEventListener("click", handlerFour);  function handlerOne() {   started = true;   setTimeout(timeoutHandler);   console.log("handler one called. fib(40) = " + fib(40)); }  function handlerTwo() {   console.log("handler two called."); }  function handlerThree() {   console.log("handler three called."); }  function handlerFour() {   console.log("handler four called.");   done = true; }  function timeoutHandler() {   console.log("timeout handler called"); }  function fib(x) {   if (x === 1 || x === 2) return 1   return fib(x - 1) + fib(x - 2); } postMessage('*', '*');
<button id="button">Click me</button>

Ok, so there is actually one frame as in EventLoop iteration to fire between the event handlers and the setTimeout callback. I like it better.

But what about that "long running frames" thing we heard about?

I'll guess you are talking about the "spin the event loop" algorithm, which is indeed meant to allow the event loop to not block all the UI in some circumstances.

First, specs only tell the implementers that it is a recommendation to enter this algorithm for long running scripts, it is not a must.

Then, this algorithm is to allow the normal EventLoop processing of events registration and UI updates, but anything related to javascript is simply resumed at the next EventLoop iteration.

So there is actually no way from js to know if we did enter this algorithm.

Even my MessageEvent driven loop can't tell, because the event handler will just get pushed to after we exit this long-running script.

Here is an attempt to put in a more graphical way, at the risk of being technically inacurate:

/**  * ...  * - handle events  *    user-click => push([cb1, cb2, cb3]) to call stack (* - paint if needed (may execute rAF callbacks if any))  *  * END OF LOOP xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94  * BEGIN OF LOOP  *  * - execute call stack  *    cb1()  *      schedule `timeoutHandler`  *      fib()  *      ...  *      ...  *      ...  *      ... <-- takes too long => "spin the event loop"  * [ pause call stack ]  * - handle events (* - paint if needed (but do not execute rAF callbacks))  *  * END OF LOOP xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94  * BEGIN OF LOOP  *  * - execute call stack  * [ resume call stack ]  *      (*fib()*)  *      ...  *      ...  *    cb2()  *    cb3()  * - handle events  *   `timeoutHandler` timed out => push to call stack (* - paint if needed (may execute rAF callbacks if any) )  *  * END OF LOOP xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94xe2x80x94  * BEGIN OF LOOP  *  * - execute call stack  *   `timeoutHandler`()  * - handle events  ...  */   
 
 
         
         
0
 
vote

La respuesta existió en realidad en https: // html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

Puntos clave:

  1. El "Frame" en "un marco representa la cantidad de trabajo que hace un navegador en una iteración de bucle de eventos, como el procesamiento de eventos, el tamaño, el cambio de tamaño, el desplazamiento, la representación, las animaciones de CSS, etc." es una iteración de procesamiento de eventos , es decir, todos los pasos de https: // html. espec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

  2. Su significado de "marco de pintura" es el paso 11 "Actualice la parte de representación".

  3. Cuándo crear un nuevo "marco de pintura" en un evento La iteración está determinada por el navegador:

    Esta especificación no exige ningún modelo en particular para seleccionar oportunidades de representación. Pero, por ejemplo, si el navegador está intentando lograr una frecuencia de actualización de 60Hz, entonces se producen oportunidades de renderizado en un máximo de cada 60 de un segundo (aproximadamente 16.7ms). Si el navegador encuentra que un contexto de navegación no puede sostener esta tasa, podría caer a las 30 oportunidades de representación más sostenibles por segundo para ese contexto de navegación, en lugar de caer en ocasiones. De manera similar, si un contexto de navegación no está visible, el agente de usuario puede decidir reducir esa página a las oportunidades de renderización 4 por segundo, o incluso menos.

    Por lo tanto, es posible que se cree un nuevo "marco de pintura" después de muchas iteración del evento (evento / procesamiento de tareas).

  4. Para la tarea larga, nuevamente, también es posible que el navegador decida no crear un nuevo "marco de pintura". (Tal vez decida manejar estos eventos inmediatamente después de la otra o es un íntimo de crear porque El contenido de la vista no cambia).

 

The answer actually existed in https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

Key points:

  1. The "frame" in "A frame represents the amount of work a browser does in one event loop iteration such as processing DOM events, resizing, scrolling, rendering, CSS animations, etc" is actually an event processing iteration, i.e. all the steps of https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model

  2. Your meaning of "paint frame" is the step 11 "Update the rendering" part.

  3. When to create a new "paint frame" in a event iteration is determined by the browser:

    This specification does not mandate any particular model for selecting rendering opportunities. But for example, if the browser is attempting to achieve a 60Hz refresh rate, then rendering opportunities occur at a maximum of every 60th of a second (about 16.7ms). If the browser finds that a browsing context is not able to sustain this rate, it might drop to a more sustainable 30 rendering opportunities per second for that browsing context, rather than occasionally dropping frames. Similarly, if a browsing context is not visible, the user agent might decide to drop that page to a much slower 4 rendering opportunities per second, or even less.

    So it is possible that a new "paint frame" is created after many event iteration(event/task processing).

  4. For the long task, again, it is also possible that the browser decides not to create a new "paint frame".(Maybe it decides to handle these event immediately after each other or it is unnessary to create because the view content does not change).

 
 

Relacionados problema

0  Salida de bucle de tiempo de espera libevente  ( Libevent timeout loop exit ) 
Tengo algunas dificultades para obtener la extensión PHP libevent para romper un bucle en un tiempo de espera. Esto es lo que tengo tan lejos en función de la...

0  GLUT: ¿CUALQUIER forma de agregar un gancho de "lectura de archivos" al bucle de eventos?  ( Glut any way to add a file readable hook to the event loop ) 
Me gustaría abrir un zócalo y colgar un evento legible en el bucle de eventos del exceso ... ¿Alguna idea de cómo hacer esto? El código de gluts estándar port...

2  Python - Cómo hacer un ciclo de eventos en la consola  ( Python how to make an event loop in the console ) 
Uso de Python 2.7, ¿cómo haría un bucle de eventos para que pueda ejecutar mi programa dentro? Mi idea es que el script se ejecutará, y el bucle de eventos de...

9  Eclipse Unmandeled Event Loop Exception, no más asas de Windows 7  ( Eclipse unhandled event loop exception no more handles windows 7 ) 
Mi eclipse se usa para desarrollar aplicaciones de Android. Funcionó bien hasta que un día, una excepción de bucle de eventos no manejada es rápido. El regist...

915  ¿Por qué SetTimeOut (FN, 0) A veces es útil?  ( Why is settimeoutfn 0 sometimes useful ) 
Recientemente me he encontrado con un insecto bastante desagradable, en el que el código estaba cargando un <select> dinámicamente a través de JavaScript. E...

7  ¿Cuál es la intención detrás de la cláusula 2.2.4 de la promesa / A + SPEC?  ( What is the intention behind clause 2 2 4 of promise a spec ) 
Cláusula 2.2.4 de la promesa / A + SPEC dice: no se llama a relulización o oneryected hasta la ejecución La pila de contexto contiene solo código de plat...

1  Código de ejecución paralelo con reactphp  ( Running code parallel with reactphp ) 
Problema: Necesito clonar / descargar varios repositorios de git, desafortunadamente, hacerlo de manera secuencial, toma edades. Tengo idea de usar el ciclo d...

-1  ¿Cómo cancelar el Loop.call_later ()?  ( How to cancel asyncios loop call later ) 
Si i Programar una devolución de llamada usando loop.call_later() (o con loop.call_at() , que se comporta de la misma manera), ¿es posible cancelar su ej...

6  Orden de bucle de eventos ASP.NET  ( Asp net event loop order ) 
He tenido tantos problemas con esto (generalmente con los eventos de carga de página que se están ejecutando antes de hacer clic en el botón, haga clic en eve...

-3  ¿Por qué la función de SETTIMEOUT no está bloqueando?  ( Why settimeout function is non blocking ) 
Si SetTimeOut es sincrónico por qué su no bloqueo? ¿En qué hilo se ejecuta si no es el hilo principal? ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos