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 comprender mejor cómo funciona RunLoop para poder elegir el método apropiado dentro de su espacio de nombres, cuando tengo que posponer la ejecución de algún código para más adelante.
- ¿Cuándo comienza Ember RunLoop? ¿Depende del enrutador, las vistas, los controladores o algo más?
- cuánto tiempo toma aproximadamente (sé que es bastante tonto 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 puede tomar un runloop)
- ¿RunLoop se está ejecutando 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 RunLoop, ¿se garantiza que todo su contenido ingresará al DOM cuando finalice el bucle?
Perdóname si estas son preguntas muy básicas, creo que entenderlas ayudará a los novatos como yo a usar mejor Ember.
Respuestas:
Actualización 9/10/2013: consulte esta visualización interactiva del ciclo de ejecución: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
Actualización 9/5/2013: todos los conceptos básicos a continuación aún están actualizados, pero a partir de esta confirmación , la implementación de Ember Run Loop se ha dividido en una biblioteca separada llamada backburner.js , con algunas diferencias de API muy pequeñas.
En primer lugar, lea 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 de RunLoop todavía se aplican generalmente a Ember; solo difieren algunos detalles de implementación. Pero, a tus preguntas:
¿Cuándo comienza Ember RunLoop? ¿Depende del enrutador, las vistas, los controladores o algo más?
Todos los eventos de usuario básicos (por ejemplo, eventos de teclado, eventos de mouse, etc.) activarán el ciclo de ejecución. Esto garantiza que cualquier cambio realizado en las propiedades vinculadas por el evento capturado (mouse / teclado / temporizador / etc.) se propague por completo a través del sistema de enlace de datos de Ember antes de devolver el control al sistema. Entonces, moviendo el mouse, presionando una tecla, haciendo clic en un botón, etc., todos inician el ciclo de ejecución.
cuánto tiempo toma aproximadamente (sé que es bastante tonto 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 puede tomar un runloop)
En ningún momento RunLoop hará un seguimiento de cuánto tiempo se tarda en propagar todos los cambios a través del sistema y luego detendrá RunLoop después de alcanzar un límite de tiempo máximo; más bien, RunLoop siempre se ejecutará hasta completarse y no se detendrá hasta que se hayan llamado a todos los temporizadores caducados, se hayan propagado los enlaces, y tal vez se hayan propagado sus enlaces, etc. Obviamente, cuantos más cambios deban propagarse desde un solo evento, más tardará RunLoop en finalizar. Aquí hay un ejemplo (bastante injusto) de cómo RunLoop puede atascarse con la propagación de cambios en comparación con otro marco (Backbone) que no tiene un bucle de ejecución: http://jsfiddle.net/jashkenas/CGSd5/. Moraleja de la historia: el RunLoop es realmente 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 quieres animar 30 círculos con Javascript a 60 fotogramas por segundo, ahí está podría ser una mejor manera de hacerlo que confiar en RunLoop de Ember.
¿RunLoop se está ejecutando 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?
No se ejecuta en todo momento; tiene que devolver el control al sistema en algún momento o de lo contrario su aplicación se bloqueará; es diferente de, por ejemplo, un ciclo de ejecución en un servidor que tiene un
while(true)
y continúa hasta el infinito hasta el servidor recibe una señal para apagarse ... el Ember RunLoop no tiene tal,while(true)
pero solo se activa en respuesta a eventos de usuario / temporizador.Si se crea una vista desde dentro de un RunLoop, ¿se garantiza que todo su contenido ingresará al DOM cuando finalice el bucle?
Veamos si podemos resolver eso. Uno de los grandes cambios de SC a Ember RunLoop es que, en lugar de ir y venir entre
invokeOnce
yinvokeLast
(que se ve en el diagrama en el primer enlace sobre el RL de SproutCore), Ember le proporciona una lista de 'colas' que, en el Durante el curso de un ciclo de ejecución, puede programar acciones (funciones que se llamarán durante el ciclo 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
el código fuente, se veEmber.run.queues = ['sync', 'actions', 'destroy', 'timers'];
, sin embargo, si se abre el depurador de JavaScript en el navegador en una aplicación Ember y evaluarEmber.run.queues
, se obtiene 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 insertarender
yafterRender
pone en cola, específicamente después de laactions
cola. Llegaré a por qué eso podría ser en un segundo. Primero, el algoritmo RunLoop:El algoritmo RunLoop es prácticamente el mismo que se describe en los artículos anteriores del ciclo de ejecución de SC:
.begin()
y.end()
, solo en Ember, querrá ejecutar su código dentroEmber.run
, que llamará internamentebegin
yend
por usted. (Solo el código de ciclo de ejecución interno en la base del código Ember todavía usabegin
yend
, por lo que debe seguir conEmber.run
)end()
se llama, RunLoop se pone en marcha para propagar cada cambio realizado por el fragmento de código pasado a laEmber.run
función. Esto incluye la propagación de los valores de las propiedades vinculadas, la representación de cambios de vista en el DOM, etc. El orden en el que se realizan estas acciones (vinculación, representación de elementos DOM, etc.) está determinado por laEmber.run.queues
matriz descrita anteriormente:sync
. Ejecutará todas las acciones programadas en lasync
cola por elEmber.run
código. Estas acciones también pueden programar más acciones para realizar durante este mismo RunLoop, y depende del RunLoop asegurarse de que realiza todas las acciones hasta que se eliminen todas las colas. La forma en que lo hace es que, al final de cada cola, RunLoop revisará todas las colas previamente descargadas y verá si se han programado nuevas acciones. Si es así, tiene que comenzar al principio de la cola más temprana con acciones programadas no realizadas y vaciar 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 se propagan los datos vinculados a través de la aplicación. Puede esperar que una vez que RunLoop se ejecute hasta su finalización, todos los datos vinculados se propagarán por completo. Entonces, ¿qué pasa con los elementos DOM?
El orden de las colas, incluidas las agregadas por la biblioteca Ember Views, es importante aquí. Note eso
render
yafterRender
venga despuéssync
, yaction
. Lasync
cola contiene todas las acciones para propagar datos enlazados. (action
, después de eso, solo se usa escasamente en la fuente Ember). Basado en el algoritmo anterior, se garantiza que para cuando RunLoop llegue a larender
cola, todas las vinculaciones de datos habrán terminado de sincronizarse. Esto es por diseño: no querrá realizar la costosa tarea de renderizar elementos DOM antessincronizar los enlaces de datos, ya que eso probablemente requeriría volver a renderizar los elementos DOM con datos actualizados, obviamente una forma muy ineficiente y propensa a errores de vaciar todas las colas de RunLoop. Por lo tanto, Ember realiza de manera inteligente todo el trabajo de enlace de datos que puede antes de representar los elementos DOM en larender
cola.Entonces, finalmente, para responder a su pregunta, sí, puede esperar que las representaciones DOM necesarias hayan tenido lugar para cuando
Ember.run
finalice el tiempo . Aquí hay un jsFiddle para demostrar: http://jsfiddle.net/machty/6p6XJ/328/Otras cosas que debe saber sobre RunLoop
Observadores contra ataduras
Es importante tener en cuenta que los observadores y enlaces, aunque tienen la funcionalidad similar de responder a los cambios en una propiedad "observada", se comportan de manera totalmente diferente en el contexto de un RunLoop. La propagación de enlaces, como hemos visto, se programa en la
sync
cola para que RunLoop la ejecute finalmente. Los observadores, por otro lado, disparan inmediatamente cuando cambia la propiedad observada sin tener que ser programados primero en una cola RunLoop. Si un observador y un enlace "observan" la misma propiedad, el observador siempre será llamado el 100% de las veces antes de que se actualice el enlace.scheduleOnce
yEmber.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 a RunLoop, múltiples acciones RunLoop idénticas se pueden unir ("eliminar rebotes", si se quiere) en una sola acción. Si observa los
run_loop.js
aspectos internos, verá que las funciones que facilitan este comportamiento son las funciones relacionadasscheduleOnce
yEm.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 inflados y derrochadores durante el ciclo de ejecución.¿Y los temporizadores?
Aunque 'temporizadores' es una de las colas predeterminadas enumeradas anteriormente, Ember solo hace referencia a la cola en sus casos de prueba RunLoop. Parece que tal cola se habría utilizado en los días de SproutCore según algunas de las descripciones de los artículos anteriores sobre los temporizadores como lo último en dispararse. En Ember, la
timers
cola no se usa. En su lugar, RunLoop puede activarse mediante unsetTimeout
evento administrado internamente (consulte el solo para ese evento, que activará RunLoop nuevamente cuando se active. Este enfoque es más eficiente que hacer que cada temporizador llame a setTimeout y se despierte a sí mismo, ya que en En este caso, solo se necesita hacer una llamada setTimeout, y RunLoop es lo suficientemente inteligente como para disparar todos los diferentes temporizadores que podrían estar sonando al mismo tiempo.invokeLaterTimers
función), que es lo suficientemente inteligente como para recorrer todos los temporizadores existentes, activar todos los que han expirado, determinar el temporizador futuro más temprano y establecer un temporizador interno.setTimeout
Más eliminación de rebotes con la
sync
colaAquí hay un fragmento del ciclo de ejecución, en medio de un ciclo a través de todas las colas en el ciclo de ejecución. Tenga en cuenta el caso especial de la
sync
cola: porquesync
es una cola particularmente volátil, en la que los datos se propagan en todas las direcciones,Ember.beginPropertyChanges()
se llama para evitar que se dispare cualquier observador, seguido de una llamada aEmber.endPropertyChanges
. Esto es sabio: si en el curso de vaciar lasync
cola, es muy posible que una propiedad en un objeto cambie varias veces antes de descansar en su valor final, y no querrá desperdiciar recursos disparando inmediatamente observadores por cada cambio. .Espero que esto ayude. Definitivamente tuve que aprender bastante solo para escribir esto, que era el punto.
fuente