Was ist Ember RunLoop und wie funktioniert es?

96

Ich versuche zu verstehen, wie Ember RunLoop funktioniert und was es zum Ticken bringt. Ich habe mir die Dokumentation angesehen , habe aber noch viele Fragen dazu. Ich bin daran interessiert, die Funktionsweise von RunLoop besser zu verstehen, damit ich innerhalb des Namensraums eine geeignete Methode auswählen kann, wenn ich die Ausführung von Code für einen späteren Zeitpunkt verschieben muss.

  • Wann startet Ember RunLoop? Ist es abhängig von Router oder Ansichten oder Controllern oder etwas anderem?
  • Wie lange dauert es ungefähr (ich weiß, dass es ziemlich albern ist, zu fragen und von vielen Dingen abhängig zu sein, aber ich suche nach einer allgemeinen Idee, oder vielleicht, wenn es eine minimale oder maximale Zeit gibt, die ein Runloop dauern kann)
  • Wird RunLoop zu jeder Zeit ausgeführt oder gibt es nur einen Zeitraum vom Beginn bis zum Ende der Ausführung an und wird möglicherweise einige Zeit nicht ausgeführt.
  • Wenn eine Ansicht aus einer RunLoop heraus erstellt wird, ist dann garantiert, dass der gesamte Inhalt bis zum Ende der Schleife in das DOM gelangt?

Verzeihen Sie mir, wenn dies sehr grundlegende Fragen sind. Ich denke, dass das Verständnis dieser Fragen Noobs wie mir hilft, Ember besser zu nutzen.

Aras
quelle
5
Es gibt keine großartigen Dokumente zur Run-Schleife. Ich werde diese Woche versuchen, ein kurzes Dia-Deck darauf zusammenzustellen.
Luke Melia
2
@LukeMelia Diese Frage braucht immer noch dringend Ihre Aufmerksamkeit und es sieht so aus, als ob einige andere Leute nach den gleichen Informationen suchen. Es wäre wunderbar, wenn Sie die Gelegenheit hätten, Ihre Erkenntnisse über RunLoop zu teilen.
Aras

Antworten:

199

Update 09.10.2013: Schauen Sie sich diese interaktive Visualisierung der Run-Schleife an: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html

Update 09.05.2013: Alle folgenden Grundkonzepte sind noch auf dem neuesten Stand, aber seit diesem Commit wurde die Ember Run Loop-Implementierung in eine separate Bibliothek namens backburner.js aufgeteilt , mit einigen sehr geringfügigen API-Unterschieden.

Lesen Sie zuerst diese:

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

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

Sie sind für Ember nicht 100% genau, aber die Kernkonzepte und die Motivation hinter dem RunLoop gelten im Allgemeinen immer noch für Ember. Nur einige Implementierungsdetails unterscheiden sich. Aber weiter zu Ihren Fragen:

Wann startet Ember RunLoop? Ist es abhängig von Router oder Ansichten oder Controllern oder etwas anderem?

Alle grundlegenden Benutzerereignisse (z. B. Tastaturereignisse, Mausereignisse usw.) starten die Ausführungsschleife. Dies garantiert, dass alle Änderungen, die durch das erfasste Ereignis (Maus / Tastatur / Timer / usw.) an gebundenen Eigenschaften vorgenommen wurden, vollständig im gesamten Datenbindungssystem von Ember übertragen werden, bevor die Kontrolle an das System zurückgegeben wird. Wenn Sie also Ihre Maus bewegen, eine Taste drücken, auf eine Schaltfläche klicken usw., wird die Run-Schleife gestartet.

Wie lange dauert es ungefähr (ich weiß, dass es ziemlich albern ist, zu fragen und von vielen Dingen abhängig zu sein, aber ich suche nach einer allgemeinen Idee, oder vielleicht, wenn es eine minimale oder maximale Zeit gibt, die ein Runloop dauern kann)

Der RunLoop wird zu keinem Zeitpunkt nachverfolgen, wie viel Zeit erforderlich ist, um alle Änderungen im System zu verbreiten, und den RunLoop nach Erreichen eines maximalen Zeitlimits anzuhalten. Stattdessen wird RunLoop immer vollständig ausgeführt und erst dann angehalten, wenn alle abgelaufenen Timer aufgerufen, Bindungen weitergegeben und möglicherweise ihre Bindungen weitergegeben wurden und so weiter. Je mehr Änderungen von einem einzelnen Ereignis übernommen werden müssen, desto länger dauert es natürlich, bis RunLoop abgeschlossen ist. Hier ist ein (ziemlich unfaires) Beispiel dafür, wie der RunLoop im Vergleich zu einem anderen Framework (Backbone) ohne Run-Schleife mit sich ausbreitenden Änderungen festgefahren werden kann: http://jsfiddle.net/jashkenas/CGSd5/. Moral der Geschichte: Der RunLoop ist sehr schnell für die meisten Dinge, die Sie jemals in Ember tun möchten, und hier liegt ein Großteil von Embers Macht, aber wenn Sie 30 Kreise mit Javascript mit 60 Bildern pro Sekunde animieren möchten, dann dort Vielleicht sind dies bessere Möglichkeiten, als sich auf Embers RunLoop zu verlassen.

Wird RunLoop zu jeder Zeit ausgeführt oder gibt es nur einen Zeitraum vom Beginn bis zum Ende der Ausführung an und wird möglicherweise einige Zeit nicht ausgeführt.

Es wird nicht immer ausgeführt - es muss irgendwann die Kontrolle an das System zurückgeben, sonst würde Ihre App hängen bleiben - es unterscheidet sich beispielsweise von einer Ausführungsschleife auf einem Server, der eine hat while(true)und bis unendlich weitergeht Der Server erhält ein Signal zum Herunterfahren ... Der Ember RunLoop hat kein solches Signal, while(true)sondern wird nur als Reaktion auf Benutzer- / Timer-Ereignisse hochgefahren .

Wenn eine Ansicht aus einer RunLoop heraus erstellt wird, ist dann garantiert, dass der gesamte Inhalt bis zum Ende der Schleife in das DOM gelangt?

Mal sehen, ob wir das herausfinden können. Eine der großen Änderungen von SC zu Ember RunLoop besteht darin, dass Ember anstelle einer Schleife zwischen invokeOnceund invokeLast(die Sie im Diagramm im ersten Link zu SproutCores RL sehen) eine Liste von Warteschlangen bereitstellt, die in der Im Verlauf einer Ausführungsschleife können Sie Aktionen (Funktionen, die während der Ausführungsschleife aufgerufen werden sollen) planen, indem Sie angeben, in welche Warteschlange die Aktion gehört (Beispiel aus der Quelle :) Ember.run.scheduleOnce('render', bindView, 'rerender');.

Wenn Sie sich run_loop.jsden Quellcode ansehen , sehen Sie Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];, aber wenn Sie Ihren JavaScript-Debugger im Browser in einer Ember-App öffnen und auswerten Ember.run.queues, erhalten Sie eine vollständigere Liste von Warteschlangen : ["sync", "actions", "render", "afterRender", "destroy", "timers"]. Ember hält ihre Codebasis ziemlich modular und ermöglicht es Ihrem Code sowie seinem eigenen Code in einem separaten Teil der Bibliothek, weitere Warteschlangen einzufügen. In diesem Fall fügt die Ember Views-Bibliothek Einfügungen renderund afterRenderWarteschlangen ein, insbesondere nach der actionsWarteschlange. Ich werde gleich erfahren, warum das so sein könnte. Zunächst der RunLoop-Algorithmus:

Der RunLoop-Algorithmus entspricht weitgehend dem in den obigen Artikeln zur SC-Laufschleife beschriebenen:

  • Sie führen Ihren Code zwischen RunLoop aus, .begin()und .end()nur in Ember möchten Sie stattdessen Ihren Code innerhalb von ausführen Ember.run, der intern beginund endfür Sie aufgerufen wird . (Nur der interne Run-Loop-Code in der Ember-Codebasis verwendet noch beginund end, daher sollten Sie einfach bei bleiben. Ember.run)
  • Nach end()dem Aufruf legt der RunLoop einen Gang ein, um jede einzelne Änderung zu übertragen, die durch den an die Ember.runFunktion übergebenen Codeabschnitt vorgenommen wird . Dies umfasst das Weitergeben der Werte gebundener Eigenschaften, das Rendern von Ansichtsänderungen im DOM usw. usw. Die Reihenfolge, in der diese Aktionen (Binden, Rendern von DOM-Elementen usw.) ausgeführt werden, wird durch das Ember.run.queuesoben beschriebene Array bestimmt :
  • Die Ausführungsschleife beginnt in der ersten Warteschlange sync. Es werden alle Aktionen ausgeführt, die syncvom Ember.runCode in der Warteschlange geplant wurden . Diese Aktionen können selbst auch weitere Aktionen planen, die während derselben RunLoop ausgeführt werden sollen, und es liegt an der RunLoop, sicherzustellen, dass jede Aktion ausgeführt wird, bis alle Warteschlangen geleert sind. Auf diese Weise durchsucht RunLoop am Ende jeder Warteschlange alle zuvor geleerten Warteschlangen und prüft, ob neue Aktionen geplant wurden. In diesem Fall muss es am Anfang der frühesten Warteschlange mit nicht ausgeführten geplanten Aktionen beginnen und die Warteschlange leeren, die Schritte weiter verfolgen und bei Bedarf von vorne beginnen, bis alle Warteschlangen vollständig leer sind.

Das ist die Essenz des Algorithmus. Auf diese Weise werden gebundene Daten über die App weitergegeben. Sie können davon ausgehen, dass alle gebundenen Daten vollständig weitergegeben werden, sobald ein RunLoop vollständig ausgeführt wird. Was ist also mit DOM-Elementen?

Die Reihenfolge der Warteschlangen, einschließlich der von der Ember Views-Bibliothek hinzugefügten, ist hier wichtig. Beachten Sie das renderund afterRenderkommen Sie nach sync, und action. Die syncWarteschlange enthält alle Aktionen zum Weitergeben gebundener Daten. ( actionwird danach in der Ember-Quelle nur noch spärlich verwendet). Basierend auf dem obigen Algorithmus wird garantiert, dass bis zum Eintreffen des RunLoop in der renderWarteschlange alle Datenbindungen synchronisiert sind. Dies ist beabsichtigt: Sie möchten die teure Aufgabe des Renderns von DOM-Elementen vorher nicht ausführenSynchronisieren der Datenbindungen, da dies wahrscheinlich ein erneutes Rendern von DOM-Elementen mit aktualisierten Daten erfordern würde - offensichtlich eine sehr ineffiziente und fehleranfällige Methode zum Leeren aller RunLoop-Warteschlangen. So durchläuft Ember auf intelligente Weise alle erforderlichen Datenbindungsarbeiten, bevor die DOM-Elemente in der renderWarteschlange gerendert werden .

Um Ihre Frage zu beantworten: Ja, Sie können davon ausgehen, dass alle erforderlichen DOM-Renderings bis zum Ende des Vorgangs stattgefunden haben Ember.run. Hier ist eine jsFiddle zur Demonstration: http://jsfiddle.net/machty/6p6XJ/328/

Weitere wichtige Informationen zum RunLoop

Beobachter gegen Bindungen

Es ist wichtig zu beachten, dass sich Beobachter und Bindungen, obwohl sie die gleiche Funktionalität haben, auf Änderungen in einer "überwachten" Eigenschaft zu reagieren, im Kontext einer RunLoop völlig anders verhalten. Wie wir gesehen haben, wird syncdie Weitergabe von Bindungen in die Warteschlange eingeplant, um schließlich von RunLoop ausgeführt zu werden. Beobachter hingegen werden sofort ausgelöst, wenn sich die überwachte Eigenschaft ändert, ohne dass sie zuerst in eine RunLoop-Warteschlange eingeplant werden müssen. Wenn ein Beobachter und eine Bindung alle dieselbe Eigenschaft "beobachten", wird der Beobachter immer 100% der Zeit vor der Aktualisierung der Bindung aufgerufen.

scheduleOnce und Ember.run.once

Eine der großen Effizienzsteigerungen in den Vorlagen für die automatische Aktualisierung von Ember basiert auf der Tatsache, dass dank RunLoop mehrere identische RunLoop-Aktionen zu einer einzigen Aktion zusammengeführt ("entprellt" werden können, wenn Sie so wollen). Wenn Sie sich die run_loop.jsInterna ansehen , werden Sie sehen, dass die Funktionen, die dieses Verhalten erleichtern, die zugehörigen Funktionen scheduleOnceund sind Em.run.once. Der Unterschied zwischen ihnen ist nicht so wichtig wie das Wissen, dass sie existieren, und wie sie doppelte Aktionen in der Warteschlange verwerfen können, um viele aufgeblähte, verschwenderische Berechnungen während der Ausführungsschleife zu verhindern.

Was ist mit Timern?

Obwohl "Timer" eine der oben aufgeführten Standardwarteschlangen ist, verweist Ember in ihren RunLoop-Testfällen nur auf die Warteschlange. Es scheint, dass eine solche Warteschlange in den SproutCore-Tagen verwendet worden wäre, basierend auf einigen Beschreibungen aus den obigen Artikeln, dass Timer das letzte sind, was ausgelöst wird. In Ember wird die timersWarteschlange nicht verwendet. Stattdessen kann der RunLoop durch ein intern verwaltetes setTimeoutEreignis (siehe invokeLaterTimersFunktion) gestartet werden , das intelligent genug ist, um alle vorhandenen Timer zu durchlaufen, alle abgelaufenen auszulösen, den frühesten zukünftigen Timer zu ermitteln und einen internen festzulegensetTimeoutNur für dieses Ereignis, bei dem der RunLoop beim Auslösen erneut hochgefahren wird. Dieser Ansatz ist effizienter, als wenn jeder Timer setTimeout aufruft und sich selbst aufweckt, da in diesem Fall nur ein setTimeout-Aufruf ausgeführt werden muss und der RunLoop intelligent genug ist, um alle verschiedenen Timer auszulösen, die möglicherweise gleichzeitig ausgelöst werden Zeit.

Weiteres Entprellen mit der syncWarteschlange

Hier ist ein Ausschnitt aus der Ausführungsschleife in der Mitte einer Schleife durch alle Warteschlangen in der Ausführungsschleife. Beachten Sie den Sonderfall für die syncWarteschlange: Da synces sich um eine besonders flüchtige Warteschlange handelt, in der Daten in alle Richtungen weitergegeben werden, Ember.beginPropertyChanges()wird aufgerufen, um zu verhindern, dass Beobachter gefeuert werden, gefolgt von einem Aufruf von Ember.endPropertyChanges. Dies ist sinnvoll: Wenn sich die syncWarteschlange während des Leeren der Warteschlange möglicherweise mehrmals ändert, bevor sie sich auf ihren endgültigen Wert stützt, möchten Sie keine Ressourcen verschwenden, indem Sie bei jeder einzelnen Änderung sofort Beobachter entlassen .

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

Hoffe das hilft. Ich musste definitiv einiges lernen, nur um dieses Ding zu schreiben, was irgendwie der Punkt war.

Alexander Wallace Matchneer
quelle
3
Großartige Zusammenfassung! Ich höre Gerüchte, dass sich das Ding "Beobachter feuern sofort" irgendwann ändern könnte, um sie wie Bindungen zu verzögern.
Jo Liss
@ JoLiss Ja, ich habe das Gefühl, ich habe seit ein paar Monaten davon gehört ... Ich bin mir nicht sicher, ob / wann es soweit sein wird.
Alexander Wallace Matchneer
1
Brendan Briggs hat beim Ember.js NYC-Treffen im Januar 2014 eine großartige Präsentation über den Run Loop gehalten. Video hier: youtube.com/watch?v=iCZUKFNXA0k
Luke Melia
1
Diese Antwort war die beste Ressource, die ich über Ember Run Loop gefunden habe, sehr gute Arbeit! Ich habe kürzlich ein umfangreiches Tutorial über die Run-Schleife veröffentlicht, das auf Ihrer Arbeit basiert und hoffentlich noch mehr Details dieses Mechanismus beschreibt. Verfügbar hier auf.netguru.co/ember-ebook-form
Kuba Niechciał