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.
Antworten:
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
invokeOnce
undinvokeLast
(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.js
den Quellcode ansehen , sehen SieEmber.run.queues = ['sync', 'actions', 'destroy', 'timers'];
, aber wenn Sie Ihren JavaScript-Debugger im Browser in einer Ember-App öffnen und auswertenEmber.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ügungenrender
undafterRender
Warteschlangen ein, insbesondere nach deractions
Warteschlange. 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:
.begin()
und.end()
nur in Ember möchten Sie stattdessen Ihren Code innerhalb von ausführenEmber.run
, der internbegin
undend
für Sie aufgerufen wird . (Nur der interne Run-Loop-Code in der Ember-Codebasis verwendet nochbegin
undend
, daher sollten Sie einfach bei bleiben.Ember.run
)end()
dem Aufruf legt der RunLoop einen Gang ein, um jede einzelne Änderung zu übertragen, die durch den an dieEmber.run
Funktion ü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 dasEmber.run.queues
oben beschriebene Array bestimmt :sync
. Es werden alle Aktionen ausgeführt, diesync
vomEmber.run
Code 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
render
undafterRender
kommen Sie nachsync
, undaction
. Diesync
Warteschlange enthält alle Aktionen zum Weitergeben gebundener Daten. (action
wird danach in der Ember-Quelle nur noch spärlich verwendet). Basierend auf dem obigen Algorithmus wird garantiert, dass bis zum Eintreffen des RunLoop in derrender
Warteschlange 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 derrender
Warteschlange 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
sync
die 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
undEmber.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.js
Interna ansehen , werden Sie sehen, dass die Funktionen, die dieses Verhalten erleichtern, die zugehörigen FunktionenscheduleOnce
und sindEm.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
timers
Warteschlange nicht verwendet. Stattdessen kann der RunLoop durch ein intern verwaltetessetTimeout
Ereignis (sieheinvokeLaterTimers
Funktion) 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 festzulegensetTimeout
Nur 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
sync
WarteschlangeHier 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
sync
Warteschlange: Dasync
es 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 vonEmber.endPropertyChanges
. Dies ist sinnvoll: Wenn sich diesync
Warteschlange 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 .Hoffe das hilft. Ich musste definitiv einiges lernen, nur um dieses Ding zu schreiben, was irgendwie der Punkt war.
quelle