¿Qué es Ember RunLoop y cómo funciona?


Estoy tratando de entender cómo funciona Ember RunLoop y qué lo hace funcionar. He mirado la documentación, pero todavía tengo muchas preguntas al respecto. Estoy interesado en entender mejor cómo funciona RunLoop para que pueda elegir el método apropiado dentro de su espacio de nombre, cuando tengo que aplazar la ejecución de algún código para más tarde.

  • Cuando se inicia Ember RunLoop. ¿Depende del enrutador o las vistas o los Controladores o algo más?
  • cuánto tiempo lo hace aproximadamente tomar (Sé que esto es bastante tonto para preguntar y depende de muchas cosas, pero estoy buscando una idea general, o tal vez si hay un tiempo mínimo o máximo que un runloop puede tomar)
  • Se ejecuta RunLoop en todo momento, o simplemente indica un período de tiempo desde el principio hasta el final de la ejecución y puede que no se ejecute durante algún tiempo.
  • Si se crea una vista desde dentro de un bucle de ejecución, ¿se garantiza que todo su contenido llegará al DOM en el momento en que el bucle termina?

Perdóname si estas son preguntas muy básicas, creo que entenderlas ayudará a noobs como yo a usar mejor la Brasa.

 97
Author: Aras, 2012-11-28

1 answers

Actualización 10/9/2013: Echa un vistazo a esta visualización interactiva del bucle de ejecución: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html

Actualización 5/9/2013: todos los conceptos básicos a continuación están todavía actualizados, pero a partir de este commit, la implementación de Ember Run Loop se ha dividido en una biblioteca separada llamada backburner.js , con algunas diferencias de API muy menores.

En primer lugar, leer estos:

Http://blog.sproutcore.com/the-run-loop-part-1 /

Http://blog.sproutcore.com/the-run-loop-part-2 /

No son 100% precisos para Ember, pero los conceptos centrales y la motivación detrás del RunLoop generalmente se aplican a Ember; solo algunos detalles de implementación difieren. Pero, a sus preguntas:

Cuando se inicia Ember RunLoop. ¿Depende del enrutador o las vistas o los Controladores o algo más?

Todos los los eventos de usuario básicos (por ejemplo, eventos de teclado, eventos de ratón, etc.) activarán el bucle de ejecución. Esto garantiza que cualquier cambio realizado en las propiedades enlazadas por el evento capturado (ratón/teclado/temporizador/etc) se propague completamente a través del sistema de enlace de datos de Ember antes de devolver el control al sistema. Así que, moviendo el ratón, pulsando una tecla, haciendo clic en un botón, etc., todos lanzan el bucle de ejecución.

¿Cuánto tiempo tarda aproximadamente (sé que esto es bastante tonto para preguntar y depende de muchas cosas, pero estoy buscando una idea general, o tal vez si hay un tiempo mínimo o máximo que un runloop puede tomar)

En ningún momento el RunLoop mantendrá un registro de cuánto tiempo se tarda en propagar todos los cambios a través del sistema y luego detener el RunLoop después de alcanzar un límite de tiempo máximo; en su lugar, el RunLoop siempre se ejecutará hasta su finalización, y no se detendrá hasta que se hayan llamado todos los temporizadores vencidos, se hayan propagado los enlaces y quizás sus enlaces se hayan propagado, y así sucesivamente. Obviamente, cuantos más cambios se deban propagar desde un solo evento, más tiempo tardará el RunLoop en finalizar. Aquí hay un ejemplo (bastante injusto) de cómo el RunLoop puede atascarse con la propagación de cambios en comparación con otro framework (Backbone) que no tiene un bucle de ejecución: http://jsfiddle.net/jashkenas/CGSd5 / . Moraleja de la historia: el RunLoop es muy rápido para la mayoría de las cosas que querrías hacer en Ember, y es donde reside gran parte del poder de Ember, pero si si desea animar 30 círculos con Javascript a 60 fotogramas por segundo, podría haber mejores formas de hacerlo que confiar en RunLoop de Ember.

Se ejecuta RunLoop en todo momento, o simplemente indica un período de tiempo desde el principio hasta el final de la ejecución y puede no ejecutarse durante algún tiempo.

No es ejecutado en todas las veces, tiene que devolver el control al sistema en algún momento o, de lo contrario su aplicación se cuelgan -- es diferente de, digamos, un bucle en un servidor que tiene un while(true) y continúa hasta infinito hasta que el servidor recibe una señal para apagarse... el RunLoop Ember no tiene tal while(true), pero solo se gira en respuesta a los eventos de usuario/temporizador.

Si se crea una vista desde dentro de un bucle de ejecución, ¿se garantiza que todo su contenido llegará al DOM cuando finalice el bucle?

Veamos si podemos averiguarlo. Uno de los grandes cambios de SC a Ember RunLoop es que, en lugar de ir y venir entre invokeOnce y invokeLast (que se ve en el diagrama en el primer enlace sobre el RL de SproutCore), Ember le proporciona una lista de 'colas' a las que, en el curso de un bucle de ejecución, puede programar acciones (funciones a las que se llamará durante el bucle de ejecución) especificando a qué cola pertenece la acción (ejemplo de la fuente: Ember.run.scheduleOnce('render', bindView, 'rerender');).

Si nos fijamos en run_loop.js en el código fuente, verá Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];, sin embargo, si abre su depurador JavaScript en el navegador en una aplicación Ember y evalúa Ember.run.queues, obtendrá una lista más completa de colas: ["sync", "actions", "render", "afterRender", "destroy", "timers"]. Ember mantiene su base de código bastante modular, y hacen posible que su código, así como su propio código en una parte separada de la biblioteca, inserte más colas. En este caso, la biblioteca Ember Views inserta colas render y afterRender, específicamente después de la cola actions. Voy a llegar a por qué podría ser en un segundo. Primero, el algoritmo RunLoop:

El algoritmo RunLoop es más o menos el mismo que se describe en los artículos de SC run loop anteriores:

  • Usted ejecute su código entre RunLoop .begin() y .end(), solo que en Ember querrá ejecutar su código dentro de Ember.run, que llamará internamente a begin y end por usted. (Solo el código interno de bucle de ejecución en la base de código Ember todavía usa begin y end, por lo que debe seguir con Ember.run)
  • Después de llamar a end(), el RunLoop se activa para propagar cada cambio realizado por el fragmento de código pasado a la función Ember.run. Esto incluye propagar los valores de bound propiedades, visualización de los cambios en el DOM, etc etc. El orden en el que se realizan estas acciones (enlace, renderización de elementos DOM, etc.) está determinado por el array Ember.run.queues descrito anteriormente:
  • El bucle de ejecución comenzará en la primera cola, que es sync. Ejecutará todas las acciones que se programaron en la cola sync por el código Ember.run. Estas acciones también pueden programar más acciones que se realizarán durante este mismo RunLoop, y depende del RunLoop realizar seguro que realiza todas las acciones hasta que todas las colas se limpian. La forma en que lo hace es, al final de cada cola, el RunLoop mirará a través de todas las colas previamente borradas y verá si se han programado nuevas acciones. Si es así, tiene que comenzar al principio de la primera cola con acciones programadas no realizadas y eliminar la cola, continuar rastreando sus pasos y comenzar de nuevo cuando sea necesario hasta que todas las colas estén completamente vacías.

Esa es la esencia del algoritmo. Así es como los datos enlazados se propagan a través de la aplicación. Puede esperar que una vez que se complete un RunLoop, todos los datos enlazados se propagarán por completo. Entonces, ¿qué pasa con los elementos DOM?

El orden de las colas, incluyendo las añadidas por la biblioteca Ember Views, es importante aquí. Observe que render y afterRender vienen después de sync, y action. La cola sync contiene todas las acciones para propagar datos enlazados. (action, después de eso, solo se usa escasamente en la fuente de la brasa). Basado en el algoritmo anterior, se garantiza que para cuando el RunLoop llegue a la cola render, todos los enlaces de datos habrán terminado de sincronizarse. Esto es por diseño: no querrá realizar la costosa tarea de renderizar elementos DOM antes de sincronizar los enlaces de datos, ya que eso probablemente requeriría volver a renderizar elementos DOM con datos actualizados obviously obviamente una forma muy ineficiente y propensa a errores de vaciar todas las colas de RunLoop. So Ember despide de forma inteligente todo el trabajo de enlace de datos que pueda antes de renderizar los elementos DOM en la cola render.

Así que, finalmente, para responder a su pregunta, sí, puede esperar que cualquier renderizado DOM necesario se haya realizado para el momento en que Ember.run termine. Aquí hay un jsFiddle para demostrar: http://jsfiddle.net/machty/6p6XJ/328 /

Otras cosas que debe saber sobre el RunLoop

Observadores vs Enlaces

Es importante tener en cuenta que Los observadores y los enlaces, aunque tienen la funcionalidad similar de responder a los cambios en una propiedad "vigilada", se comportan de manera totalmente diferente en el contexto de un RunLoop. La propagación de enlace, como hemos visto, se programa en la cola sync para ser finalmente ejecutada por el RunLoop. Los observadores, por otro lado, disparan inmediatamente cuando la propiedad observada cambia sin tener que ser programada primero en una cola de RunLoop. Si un Observador y un enlace todos " ver " la misma propiedad, siempre se llamará al observador 100% antes de que se actualice el enlace.

scheduleOnce y Ember.run.once

Uno de los grandes aumentos de eficiencia en las plantillas de actualización automática de Ember se basa en el hecho de que, gracias al RunLoop, se pueden fusionar múltiples acciones de RunLoop idénticas ("rebotadas", si se quiere) en una sola acción. Si observas el funcionamiento interno de run_loop.js, verás que las funciones que facilitan este comportamiento son las funciones relacionadas scheduleOnce y Em.run.once. La diferencia entre ellos no es tan importante como saber que existen, y cómo pueden descartar acciones duplicadas en la cola para evitar una gran cantidad de cálculos hinchados y derrochadores durante el bucle de ejecución.

¿Qué pasa con los temporizadores?

Aunque 'temporizadores' es una de las colas predeterminadas listadas anteriormente, Ember solo hace referencia a la cola en sus casos de prueba de RunLoop. Parece que tal cola se habría utilizado en los días de SproutCore basado en algunas de las descripciones de lo anterior artículos sobre temporizadores siendo lo último en disparar. En Ember, la cola timers no se usa. En su lugar, el RunLoop puede ser girado por un evento setTimeout administrado internamente (consulte la función invokeLaterTimers), que es lo suficientemente inteligente como para recorrer todos los temporizadores existentes, disparar todos los que han expirado, determinar el temporizador futuro más temprano y establecer un setTimeout interno para ese evento solamente, que hará girar el RunLoop nuevamente cuando se dispare. Este enfoque es más eficiente que tener cada llamada de temporizador setTimeout y wake itself up, ya que en este caso, solo se necesita realizar una llamada setTimeout, y el RunLoop es lo suficientemente inteligente como para disparar todos los diferentes temporizadores que podrían estar activándose al mismo tiempo.

Debouncing adicional con la cola sync

Aquí hay un fragmento del bucle de ejecución, en medio de un bucle a través de todas las colas en el bucle de ejecución. Tenga en cuenta el caso especial para la cola sync: porque sync es una cola particularmente volátil, en la que los datos se propagan en cada dirección, Ember.beginPropertyChanges() es llamada para evitar que cualquier observador sea disparado, seguido por una llamada a Ember.endPropertyChanges. Esto es prudente: si en el curso de la descarga de la cola sync, es completamente posible que una propiedad de un objeto cambie varias veces antes de descansar en su valor final, y no querrá desperdiciar recursos disparando observadores inmediatamente por cada cambio.

if (queueName === 'sync') 
{
    log = Ember.LOG_BINDINGS;

    if (log) 
    {
        Ember.Logger.log('Begin: Flush Sync Queue');
    }

    Ember.beginPropertyChanges();
    Ember.tryFinally(tryable, Ember.endPropertyChanges);

    if (log) 
    { 
        Ember.Logger.log('End: Flush Sync Queue'); 
    }
} 
else 
{
   forEach.call(queue, iter);
}

Espero que esto ayude. Definitivamente tuve que aprender un poco sólo para escribir esta cosa, que fue amable del punto.

 199
Author: Alexander Wallace Matchneer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-23 12:49:09