J'essaie de comprendre comment fonctionne Ember RunLoop et ce qui le motive. J'ai regardé la documentation , mais j'ai encore de nombreuses questions à ce sujet. Je suis intéressé à mieux comprendre le fonctionnement de RunLoop afin de pouvoir choisir la méthode appropriée dans son espace de nom, lorsque je dois reporter l'exécution d'un code à une date ultérieure.
- Quand Ember RunLoop démarre-t-il. Dépend-il du routeur ou des vues ou des contrôleurs ou de quelque chose d'autre?
- combien de temps cela prend-il approximativement (je sais que c'est plutôt idiot de demander et dépend de beaucoup de choses mais je cherche une idée générale, ou peut-être s'il y a un temps minimum ou maximum qu'une boucle d'exécution peut prendre)
- RunLoop est-il exécuté à tout moment ou indique-t-il simplement une période allant du début à la fin de l'exécution et peut ne pas s'exécuter pendant un certain temps.
- Si une vue est créée à partir d'un RunLoop, est-il garanti que tout son contenu sera intégré dans le DOM à la fin de la boucle?
Pardonnez-moi si ce sont des questions très basiques, je pense que les comprendre aidera les noobs comme moi à mieux utiliser Ember.
Réponses:
Mise à jour 10/9/2013: Découvrez cette visualisation interactive de la boucle d'exécution: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
Mise à jour du 09/05/2013: tous les concepts de base ci-dessous sont toujours à jour, mais à partir de cette validation , l'implémentation d'Ember Run Loop a été divisée en une bibliothèque séparée appelée backburner.js , avec quelques différences d'API très mineures.
Tout d'abord, lisez ceci:
http://blog.sproutcore.com/the-run-loop-part-1/
http://blog.sproutcore.com/the-run-loop-part-2/
Ils ne sont pas 100% précis à Ember, mais les concepts de base et la motivation derrière le RunLoop s'appliquent toujours généralement à Ember; seuls quelques détails d'implémentation diffèrent. Mais, passons à vos questions:
Quand Ember RunLoop démarre-t-il. Dépend-il du routeur ou des vues ou des contrôleurs ou de quelque chose d'autre?
Tous les événements utilisateur de base (par exemple les événements de clavier, les événements de souris, etc.) déclencheront la boucle d'exécution. Cela garantit que toutes les modifications apportées aux propriétés liées par l'événement capturé (souris / clavier / minuterie / etc.) sont entièrement propagées dans le système de liaison de données d'Ember avant de retourner le contrôle au système. Ainsi, déplacer votre souris, appuyer sur une touche, cliquer sur un bouton, etc., tous lancent la boucle d'exécution.
combien de temps cela prend-il approximativement (je sais que c'est plutôt idiot de demander et dépend de beaucoup de choses mais je cherche une idée générale, ou peut-être s'il y a un temps minimum ou maximum qu'une boucle d'exécution peut prendre)
A aucun moment, le RunLoop ne gardera une trace du temps qu'il faut pour propager tous les changements à travers le système, puis arrêtera le RunLoop après avoir atteint une limite de temps maximum; au contraire, le RunLoop fonctionnera toujours jusqu'à la fin, et ne s'arrêtera pas tant que tous les minuteurs expirés n'auront pas été appelés, les liaisons propagées, et peut-être leurs liaisons propagées, et ainsi de suite. Évidemment, plus il y a de changements à propager à partir d'un seul événement, plus le RunLoop mettra du temps à se terminer. Voici un exemple (assez injuste) de la façon dont le RunLoop peut s'enliser avec la propagation des changements par rapport à un autre framework (Backbone) qui n'a pas de boucle d'exécution: http://jsfiddle.net/jashkenas/CGSd5/. Morale de l'histoire: le RunLoop est très rapide pour la plupart des choses que vous voudriez faire dans Ember, et c'est là que réside une grande partie du pouvoir d'Ember, mais si vous vous trouvez à vouloir animer 30 cercles avec Javascript à 60 images par seconde, là pourrait être une meilleure façon de procéder que de compter sur le RunLoop d'Ember.
RunLoop est-il exécuté à tout moment ou indique-t-il simplement une période allant du début à la fin de l'exécution et peut ne pas s'exécuter pendant un certain temps.
Il n'est pas exécuté à tout moment - il doit renvoyer le contrôle au système à un moment donné, sinon votre application se bloquerait - c'est différent, par exemple, d'une boucle d'exécution sur un serveur qui a un
while(true)
et continue pendant l'infini jusqu'à le serveur reçoit un signal d'arrêt ... l'Ember RunLoop n'en a pas,while(true)
mais n'est lancé qu'en réponse aux événements utilisateur / minuterie.Si une vue est créée à partir d'un RunLoop, est-il garanti que tout son contenu sera intégré dans le DOM à la fin de la boucle?
Voyons si nous pouvons comprendre cela. L' un des grands changements de SC à Ember runloop est que, au lieu de boucle et - vient entre
invokeOnce
etinvokeLast
(que vous voyez dans le diagramme dans le premier lien sur la RL SproutCore), Ember vous fournit une liste des « files d' attente » qui, dans le au cours d'une boucle d'exécution, vous pouvez planifier des actions (fonctions à appeler pendant la boucle d'exécution) en spécifiant à quelle file d'attente l'action appartient (exemple de la source:)Ember.run.scheduleOnce('render', bindView, 'rerender');
.Si vous regardez
run_loop.js
dans le code source, vous voyezEmber.run.queues = ['sync', 'actions', 'destroy', 'timers'];
, mais si vous ouvrez votre débogueur JavaScript dans le navigateur dans une application Ember et d' évaluerEmber.run.queues
, vous obtenez une liste plus complète des files d' attente:["sync", "actions", "render", "afterRender", "destroy", "timers"]
. Ember garde sa base de code assez modulaire et permet à votre code, ainsi qu'à son propre code dans une partie distincte de la bibliothèque, d'insérer plus de files d'attente. Dans ce cas, la Ember Vues inserts bibliothèquerender
et lesafterRender
files d' attente, en particulier après laactions
file d' attente. Je vais comprendre pourquoi cela pourrait être dans une seconde. Tout d'abord, l'algorithme RunLoop:L'algorithme RunLoop est à peu près le même que celui décrit dans les articles de boucle d'exécution SC ci-dessus:
.begin()
et.end()
, seulement dans Ember, vous voudrez plutôt exécuter votre code à l'intérieurEmber.run
, qui appellera en internebegin
etend
pour vous. (Seul le code de boucle d'exécution interne dans la base de code Ember utilise toujoursbegin
etend
, vous devez donc vous en tenir àEmber.run
)end()
été appelé, le RunLoop passe à la vitesse supérieure pour propager chaque modification apportée par le morceau de code passé à laEmber.run
fonction. Cela inclut la propagation des valeurs des propriétés liées, le rendu des modifications de vue dans le DOM, etc. L'ordre dans lequel ces actions (liaison, rendu des éléments DOM, etc.) sont effectuées est déterminé par leEmber.run.queues
tableau décrit ci-dessus:sync
. Il exécutera toutes les actions planifiées dans lasync
file d'attente par leEmber.run
code. Ces actions peuvent elles-mêmes planifier d'autres actions à effectuer au cours de ce même RunLoop, et c'est au RunLoop de s'assurer qu'il exécute chaque action jusqu'à ce que toutes les files d'attente soient vidées. Pour ce faire, à la fin de chaque file d'attente, le RunLoop examinera toutes les files d'attente précédemment vidées et verra si de nouvelles actions ont été planifiées. Si tel est le cas, il doit démarrer au début de la première file d'attente avec des actions planifiées non exécutées et vider la file d'attente, continuer à suivre ses étapes et recommencer si nécessaire jusqu'à ce que toutes les files d'attente soient complètement vides.C'est l'essence de l'algorithme. C'est ainsi que les données liées sont propagées via l'application. Vous pouvez vous attendre qu'une fois qu'un RunLoop s'exécute, toutes les données liées seront entièrement propagées. Alors, qu'en est-il des éléments DOM?
L'ordre des files d'attente, y compris celles ajoutées par la bibliothèque Ember Views, est important ici. Remarquez cela
render
etafterRender
venez aprèssync
, etaction
. Lasync
file d'attente contient toutes les actions de propagation des données liées. (action
, après cela, n'est que rarement utilisé dans la source Ember). Sur la base de l'algorithme ci-dessus, il est garanti qu'au moment où le RunLoop arrive dans larender
file d'attente, toutes les liaisons de données auront terminé la synchronisation. C'est par conception: vous ne voudriez pas effectuer la tâche coûteuse de rendu des éléments DOM avantsynchroniser les liaisons de données, car cela nécessiterait probablement de restituer les éléments DOM avec des données mises à jour - évidemment une manière très inefficace et sujette aux erreurs de vider toutes les files d'attente RunLoop. Ainsi, Ember analyse intelligemment tout le travail de liaison de données qu'il peut avant de rendre les éléments DOM dans larender
file d'attente.Donc, enfin, pour répondre à votre question, oui, vous pouvez vous attendre à ce que tous les rendus DOM nécessaires aient eu lieu à la
Ember.run
fin. Voici un jsFiddle à démontrer: http://jsfiddle.net/machty/6p6XJ/328/Autres choses à savoir sur le RunLoop
Observateurs vs liaisons
Il est important de noter que les observateurs et les liaisons, tout en ayant la fonctionnalité similaire de répondre aux changements dans une propriété «surveillée», se comportent totalement différemment dans le contexte d'un RunLoop. La propagation de liaison, comme nous l'avons vu, est planifiée dans la
sync
file d'attente pour être finalement exécutée par le RunLoop. Les observateurs, en revanche, se déclenchent immédiatement lorsque la propriété surveillée change sans avoir à être programmés au préalable dans une file d'attente RunLoop. Si un observateur et une liaison «surveillent» tous la même propriété, l'observateur sera toujours appelé 100% du temps avant que la liaison ne soit mise à jour.scheduleOnce
etEmber.run.once
Un des gros gains d'efficacité dans les modèles de mise à jour automatique d'Ember est basé sur le fait que, grâce au RunLoop, plusieurs actions RunLoop identiques peuvent être fusionnées ("déboncées", si vous voulez) en une seule action. Si vous regardez dans les éléments
run_loop.js
internes, vous verrez que les fonctions qui facilitent ce comportement sont les fonctions associéesscheduleOnce
etEm.run.once
. La différence entre eux n'est pas si importante que de savoir qu'ils existent, et comment ils peuvent ignorer les actions en double dans la file d'attente pour éviter beaucoup de calculs gonflés et inutiles pendant la boucle d'exécution.Et les minuteries?
Même si 'timers' est l'une des files d'attente par défaut répertoriées ci-dessus, Ember fait uniquement référence à la file d'attente dans leurs cas de test RunLoop. Il semble qu'une telle file d'attente aurait été utilisée à l'époque de SproutCore sur la base de certaines des descriptions des articles ci-dessus concernant les minuteries étant la dernière chose à tirer. Dans Ember, la
timers
file d'attente n'est pas utilisée. Au lieu de cela, le RunLoop peut être lancé par unsetTimeout
événement géré en interne (voir lainvokeLaterTimers
fonction), qui est suffisamment intelligent pour parcourir tous les minuteurs existants, déclencher tous ceux qui ont expiré, déterminer le premier minuteur futur et définir unsetTimeout
pour cet événement uniquement, ce qui fera redémarrer le RunLoop lorsqu'il se déclenchera. Cette approche est plus efficace que d'avoir chaque appel de minuterie setTimeout et se réveiller, car dans ce cas, un seul appel setTimeout doit être effectué, et le RunLoop est suffisamment intelligent pour déclencher tous les différents minuteurs qui pourraient se déclencher en même temps. temps.Plus de rebond avec la
sync
file d'attenteVoici un extrait de la boucle d'exécution, au milieu d'une boucle à travers toutes les files d'attente de la boucle d'exécution. Notez le cas particulier de la
sync
file d'attente: parcesync
qu'une file d'attente particulièrement volatile, dans laquelle les données sont propagées dans toutes les directions,Ember.beginPropertyChanges()
est appelée pour empêcher le déclenchement de tout observateur, suivi d'un appel àEmber.endPropertyChanges
. C'est sage: si au cours du vidage de lasync
file d'attente, il est tout à fait possible qu'une propriété sur un objet change plusieurs fois avant de se reposer sur sa valeur finale, et vous ne voudriez pas gaspiller des ressources en tirant immédiatement des observateurs pour chaque changement. .J'espère que cela t'aides. J'ai vraiment dû apprendre un peu pour écrire ce truc, ce qui était un peu le point.
la source