97 votes

Qu'est-ce que Ember RunLoop et comment fonctionne-t-il ?

J'essaie de comprendre comment fonctionne le RunLoop d'Ember et ce qui le fait fonctionner. J'ai regardé la documentation mais j'ai encore beaucoup de questions à ce sujet. Je souhaite mieux comprendre le fonctionnement de RunLoop afin de pouvoir choisir la méthode appropriée dans son espace de noms, lorsque je dois reporter l'exécution d'un code à une date ultérieure.

  • Quand est-ce que le RunLoop Ember commence. Dépend-il de Router, Views, Controllers ou autre chose ?
  • combien de temps cela prend-il approximativement (je sais que c'est plutôt stupide à demander et que cela 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'un runloop peut prendre)
  • RunLoop est-il exécuté en permanence, ou indique-t-il seulement une période de temps entre le début et la fin de l'exécution et peut ne pas s'exécuter pendant un certain temps.
  • Si une vue est créée à l'intérieur d'un RunLoop, est-il garanti que tout son contenu sera placé dans le DOM à la fin de la boucle ?

Pardonnez-moi si ces questions sont très basiques, je pense que les comprendre aidera les novices comme moi à mieux utiliser Ember.

201voto

Mise à jour 10/9/2013 : Consultez cette visualisation interactive de la boucle de course : https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html

Mise à jour 5/9/2013 : tous les concepts de base ci-dessous sont toujours d'actualité, mais à partir du cet engagement la mise en œuvre de la boucle d'exécution d'Ember a été séparée en une bibliothèque distincte appelée backburner.js avec quelques différences mineures dans l'API.

Tout d'abord, lisez ça :

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

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

Ils ne sont pas à 100 % fidèles à Ember, mais les concepts fondamentaux et la motivation qui sous-tendent le RunLoop s'appliquent généralement à Ember ; seuls certains détails de mise en œuvre diffèrent. Mais, pour en venir à vos questions :

Quand est-ce que Ember RunLoop commence. Dépend-il de Router, Views, Controllers ou autre chose ?

Tous les événements utilisateur de base (par exemple, les événements clavier, 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/minuteur/etc) sont entièrement propagées dans le système de liaison de données d'Ember avant de rendre le contrôle au système. Ainsi, le fait de déplacer la souris, d'appuyer sur une touche, de cliquer sur un bouton, etc. lance la boucle d'exécution.

combien de temps cela prend-il approximativement (je sais que c'est plutôt stupide à demander et que cela 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'un runloop peut prendre)

À aucun moment, le RunLoop ne tiendra compte du temps nécessaire pour propager tous les changements à travers le système et ne s'arrêtera pas après avoir atteint une limite de temps maximale ; au contraire, le RunLoop s'exécutera toujours jusqu'à la fin, et ne s'arrêtera pas tant que tous les délais expirés n'auront pas été appelés, les liaisons propagées, et peut-être même leur les liaisons propagées, et ainsi de suite. Évidemment, plus il y a de changements qui doivent être propagés à partir d'un seul événement, plus le RunLoop prendra du temps pour se terminer. Voici un exemple (assez injuste) de la façon dont le RunLoop peut s'enliser dans la propagation des changements par rapport à un autre framework (Backbone) qui n'a pas de RunLoop : http://jsfiddle.net/jashkenas/CGSd5/ . Morale de l'histoire : le RunLoop est très rapide pour la plupart des choses que vous voulez faire dans Ember, et c'est là que réside une grande partie de la puissance d'Ember, mais si vous voulez animer 30 cercles avec Javascript à 60 images par seconde, il y a peut-être de meilleures façons de procéder que de s'appuyer sur le RunLoop d'Ember.

RunLoop est-il exécuté en permanence, ou indique-t-il seulement une période de temps entre le début et la fin de l'exécution et peut ne pas s'exécuter pendant un certain temps.

Elle n'est pas exécutée en permanence - elle doit rendre le contrôle au système à un moment ou à un autre, sinon votre application se bloquerait. while(true) et continue à l'infini jusqu'à ce que le serveur reçoive le signal de s'arrêter... le RunLoop d'Ember n'a pas ce genre d'obligation. while(true) mais il n'est activé qu'en réponse à des événements liés à l'utilisateur ou au temps.

Si une vue est créée à l'intérieur d'un RunLoop, est-il garanti que tout son contenu se retrouvera dans le DOM au moment où la boucle se termine ?

Voyons si on peut trouver une solution. L'un des grands changements de SC à Ember RunLoop est que, au lieu de faire des boucles entre invokeOnce y invokeLast (que vous voyez dans le diagramme du premier lien sur la RL de SproutCore), Ember vous fournit une liste de "files d'attente" auxquelles, au cours d'une boucle d'exécution, vous pouvez programmer des actions (fonctions à appeler pendant la boucle d'exécution) en spécifiant à quelle file d'attente l'action appartient (exemple tiré de la source : Ember.run.scheduleOnce('render', bindView, 'rerender'); ).

Si vous regardez run_loop.js dans le code source, vous voyez Ember.run.queues = ['sync', 'actions', 'destroy', 'timers']; Pourtant, si vous ouvrez votre débogueur JavaScript dans le navigateur dans une application Ember et que vous évaluez Ember.run.queues vous obtenez une liste plus complète de files d'attente : ["sync", "actions", "render", "afterRender", "destroy", "timers"] . Ember garde sa base de code assez modulaire, et ils rendent possible pour votre code, ainsi que son propre code dans une partie séparée de la bibliothèque, d'insérer plus de files d'attente. Dans ce cas, la bibliothèque Ember Views insère render y afterRender les files d'attente, notamment après le actions queue. Je vais vous expliquer pourquoi dans une seconde. D'abord, l'algorithme RunLoop :

L'algorithme RunLoop est à peu près le même que celui décrit dans les articles sur la boucle d'exécution SC ci-dessus :

  • Vous exécutez votre code entre RunLoop .begin() y .end() mais dans Ember, vous voudrez plutôt exécuter votre code à l'intérieur de Ember.run qui appellera en interne begin y end pour vous. (Seul le code interne de la boucle d'exécution dans la base de code d'Ember utilise encore begin y end donc vous devriez vous contenter de Ember.run )
  • Après end() est appelé, le RunLoop se met alors en marche pour propager chaque changement effectué par le morceau de code passé à la fonction Ember.run fonction. Cela inclut la propagation des valeurs des propriétés liées, le rendu des changements de vue dans le DOM, etc. etc. L'ordre dans lequel ces actions (liaison, rendu des éléments du DOM, etc.) sont effectuées est déterminé par la fonction Ember.run.queues décrite ci-dessus :
  • La boucle d'exécution démarrera sur la première file d'attente, qui est sync . Il exécutera toutes les actions qui ont été planifiées dans la section sync par la Ember.run code. Ces actions peuvent elles-mêmes programmer d'autres actions à exécuter pendant 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 examine toutes les files d'attente précédemment vidées et vérifie si de nouvelles actions ont été programmées. Si c'est le cas, il doit commencer au début de la première file d'attente avec des actions programmées non exécutées et vider la file d'attente, en continuant à suivre ses étapes et en recommençant si nécessaire jusqu'à ce que toutes les files d'attente soient complètement vides.

C'est l'essence même de l'algorithme. C'est ainsi que les données liées sont propagées dans l'application. Vous pouvez vous attendre à ce qu'une fois le RunLoop terminé, toutes les données liées soient entièrement propagées. Alors, qu'en est-il des éléments du DOM ?

L'ordre des files d'attente, y compris celles ajoutées par la bibliothèque Ember Views, est important ici. Notez que render y afterRender venir après sync et action . Le site sync contient toutes les actions de propagation des données liées. ( action après cela, n'est que très peu utilisé dans le code source d'Ember). Sur la base de l'algorithme ci-dessus, il est garanti qu'au moment où le RunLoop arrive à l'étape render toutes les liaisons de données auront fini de se synchroniser. Ceci est voulu : vous ne voudriez pas effectuer la tâche coûteuse de rendre les éléments du DOM avant synchroniser les liaisons de données, car cela nécessiterait probablement de rendre à nouveau les éléments du DOM avec les données mises à jour - ce qui est évidemment une façon très inefficace et sujette aux erreurs de vider toutes les files d'attente du RunLoop. C'est pourquoi Ember effectue intelligemment tout le travail de liaison de données qu'il peut faire avant de rendre les éléments du DOM dans la file d'attente du RunLoop. render queue.

Enfin, pour répondre à votre question, oui, vous pouvez vous attendre à ce que tous les rendus nécessaires de DOM aient eu lieu au moment de la mise en œuvre de l'initiative. Ember.run finitions. Voici un jsFiddle pour démontrer : http://jsfiddle.net/machty/6p6XJ/328/

Autres choses à savoir sur le RunLoop

Observateurs et liaisons

Il est important de noter que les Observers et les Bindings, bien qu'ayant une fonctionnalité similaire de réponse aux changements d'une propriété "surveillée", se comportent de manière totalement différente dans le contexte d'un RunLoop. La propagation des liaisons, comme nous l'avons vu, est planifiée dans le processus de l'observateur. sync pour être éventuellement exécuté par le RunLoop. Les observateurs, quant à eux, lancent immédiatement lorsque la propriété observée change sans avoir à être programmé au préalable dans une file d'attente RunLoop. Si un observateur et une liaison "surveillent" la même propriété, l'observateur sera toujours appelé 100% du temps avant que la liaison ne soit mise à jour.

scheduleOnce y Ember.run.once

L'un des grands gains d'efficacité des modèles de mise à jour automatique d'Ember repose sur le fait que, grâce à la RunLoop, plusieurs actions RunLoop identiques peuvent être regroupées ("débondées", si vous voulez) en une seule action. Si vous regardez dans la section run_loop.js vous verrez que les fonctions qui facilitent ce comportement sont les fonctions connexes suivantes scheduleOnce y Em.run.once . La différence entre eux n'est pas aussi importante que le fait de savoir qu'ils existent, et comment ils peuvent écarter les actions dupliquées dans la file d'attente pour éviter beaucoup de calculs inutiles pendant la boucle d'exécution.

Et les minuteurs ?

Même si "timers" est l'une des files d'attente par défaut énumérées ci-dessus, Ember ne fait référence à cette file d'attente que dans ses scénarios de test RunLoop. Il semble qu'une telle file d'attente aurait été utilisée à l'époque de SproutCore, si l'on se fie à certaines des descriptions des articles susmentionnés, selon lesquelles les minuteries sont les derniers éléments à déclencher. Dans Ember, la timers n'est pas utilisée. Au lieu de cela, la boucle d'exécution peut être lancée par une file d'attente gérée en interne. setTimeout (voir l invokeLaterTimers ), qui est suffisamment intelligente pour parcourir en boucle toutes les minuteries existantes, déclencher toutes celles qui ont expiré, déterminer la minuterie future la plus proche, et définir une fonction interne setTimeout pour cet événement uniquement, qui relancera le RunLoop lorsqu'il se déclenchera. Cette approche est plus efficace que de demander à chaque minuterie d'appeler setTimeout et de se réveiller, car dans ce cas, un seul appel setTimeout doit être effectué, et la boucle d'exécution est suffisamment intelligente pour déclencher toutes les différentes minuteries qui peuvent se déclencher en même temps.

Un déboulonnage supplémentaire avec le sync file d'attente

Voici un extrait de la boucle d'exécution, au milieu d'une boucle passant par toutes les files d'attente de la boucle d'exécution. Notez le cas particulier de l'élément sync file d'attente : parce que sync est une file d'attente particulièrement volatile, dans laquelle les données se propagent dans toutes les directions, Ember.beginPropertyChanges() est appelé pour empêcher tout observateur d'être déclenché, suivi d'un appel à Ember.endPropertyChanges . C'est sage : si au cours de la chasse d'eau le sync queue, il est tout à fait possible qu'une propriété d'un objet change plusieurs fois avant de prendre sa valeur finale, et vous ne voudriez pas gaspiller des ressources en déclenchant immédiatement des observateurs pour chaque changement.

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);
}

J'espère que cela vous aidera. J'ai vraiment dû apprendre pas mal de choses juste pour écrire ce truc, ce qui était un peu le but.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X