2014-11-03 5 views
6

Es ist dieser Artikel, den ich vor langer Zeit sah: https://coderwall.com/p/ngisma
Es beschreibt ein Verfahren, das $ gilt, wenn wir nicht in einer Anwendung oder verdauen Phase auslöst.Vergleich Weisen verdauen verursacht

$scope.safeApply = function(fn) { 
    var phase = this.$root.$$phase; 
    if(phase == '$apply' || phase == '$digest') { 
    if(fn && (typeof(fn) === 'function')) { 
     fn(); 
    } 
    } else { 
    this.$apply(fn); 
    } 
}; 

Angular hat die $scope.$evalAsync Methode (von 1.2.14 genommen):

$evalAsync: function(expr) { 
      // if we are outside of an $digest loop and this is the first time we are scheduling async 
      // task also schedule async auto-flush 
      if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { 
       $browser.defer(function() { 
       if ($rootScope.$$asyncQueue.length) { 
        $rootScope.$digest(); 
       } 
       }); 
      } 

      this.$$asyncQueue.push({scope: this, expression: expr}); 
      } 

welche Anrufe verdauen, wenn wir nicht in einer Phase sind und fügt den aktuellen Aufruf zum asyncQueue.

Es gibt auch die Methoden $ apply, $ digest und $ timeout. Es ist verwirrend.
Was ist der Unterschied zwischen allen genannten Möglichkeiten, um einen Digest-Zyklus auszulösen (ein Dirty Check und eine Datenbindung)?
Was ist der Anwendungsfall für jede Methode?
Ist safeApply() immer noch sicher? :)
Welche Alternative haben wir anstelle von safeApply() (wenn wir $ mitten in einem Digest-Zyklus aufrufen)?

+0

Dies ist eine sehr gute Frage, leider passt es wahrscheinlich nicht zum Format von Stack Overflow. – Sacho

+0

Sehen Sie, ob dies hilft: http://www.sitepoint.com/understanding-angulars-apply-digest/ –

+0

@Sacho Was meinst du? – Naor

Antwort

2

Als eine High-Level-Einführung würde ich sagen, dass es selten erforderlich ist, Ihren eigenen Digest-Zyklus tatsächlich zu initiieren, da eckig die meisten Fälle behandelt.

Lassen Sie uns auf die Frage eingehen.

Als sehr hohen Niveau, das verdauen $ sieht Schleife dies wie:

Do: 
- - - If asyncQueue.length, flush asyncQueue. 
- - - Trigger all $watch handlers. 
- - - Check for "too many" $digest iterations. 
While: (Dirty data || asyncQueue.length) 

Also im Grunde $evalAsync zum asyncQueue die Funktion erweitert und defering ein verdauen, wenn sie ihn braucht. Wenn es jedoch bereits in einem Digest-Zyklus ist, wird es die asyncQueue leeren und es wird nur die Funktion aufrufen.

Sie können feststellen, dass dies sehr ähnlich wie die safeApply ist. Ein Unterschied besteht darin, dass die Funktion nicht zu der asyncQueue hinzugefügt wird, sondern nur aufgerufen wird, was beispielsweise in der Mitte eines Zyklus passieren kann. Der andere Unterschied besteht darin, dass es eine $$ Variable exponiert, die intern sein soll.

Der wichtigste Unterschied aber zwischen $evalAsync und $apply von $digest (Ich werde zu $timeout unten bekommen) ist, dass $evalAsync und $apply am $rootScope den Digest beginnt, aber sie rufen Sie die $digest auf jedem Umfang. Sie müssen Ihren individuellen Fall bewerten, wenn Sie denken, dass Sie diese Flexibilität benötigen.

Die Verwendung von $timeout ist wieder sehr ähnlich zu $evalAsync, außer dass es es immer aus dem aktuellen Digest-Zyklus verschiebt, falls es einen gibt.

$timeout(function() { 
    console.log("$timeout 1"); 
}); 
$scope.$evalAsync(function($scope) { 
    console.log("$evalAsync"); 
}); 

Wenn Sie bereits in einem Digest-Zyklus geben Ihnen

$evalAsync 
$timeout 1 

Auch wenn sie in der entgegengesetzten Reihenfolge aufgerufen werden, da das Timeout einem zum nächsten verzögert Zyklus verdauen, die es instanziiert.

EDIT Für die Fragen in den Kommentar. Der größte Unterschied zwischen $apply und $evalAsync, soweit ich sagen kann, ist, dass $apply tatsächlich den $digest Zyklus auslöst. Für Sie bedeutet dies, dass Sie sicher sein müssen, dass Sie $apply aufrufen, wenn Sie sich nicht in einem Digest-Zyklus befinden. Nur für Transparenz, ist der folgende Code gilt auch:

$scope.$apply(function() { 
    $scope.$evalAsync(function() { 
    }); 
}); 

, die die Anwendung Funktion aufrufen, die no-op-Funktion zum $asyncQueue und den Digest-Zyklus beginnen. Die docs sagen, es wird empfohlen, $apply zu verwenden, wenn sich das Modell ändert, was in Ihrem $evalAsync

Der Unterschied zwischen fn(); $scope.apply() und $scope.apply(fn) passieren könnte, ist nur, dass die $scope.apply(fn) tut einen Versuch Fang für die Funktion und explizit verwendet $exceptionHandler. Darüber hinaus könnten Sie tatsächlich versuchen, Digest-Zyklen in fn zu verursachen, die ändern, wie die Anwendung behandelt wird.

Ich würde auch gerne auf diese Zeit hinweisen, dass es noch komplizierter ist in 1.3 vorausgesetzt, es bleibt so. Es wird eine Funktion namens $applyAsync geben, die verwendet wird, um ankommende Anrufe zu verzögern (reference).

Dieser Beitrag zusammengestellt einige Informationen aus This blog und diese so post plus einige meiner Erfahrung.

Hoffe das half!

+0

Cool! Können Sie etwas über die Unterschiede zwischen $ apply, $ evalAsync und dem Unterschied zwischen '$ apply (fn)' erklären? und 'fn(); sich bewerben();' ? – Naor

+0

Ein paar Sachen zu der Antwort hinzugefügt. – hassassin

+0

Ist es sicher, $ evalAsyc (fn) anstelle von $ apply (fn) aufzurufen? Dies verhindert, dass der Fehler "$ apply in progress" angewendet wird. Ist es auch eine gute Übung? Wenn nicht, warum? Vielen Dank für Ihre Antwort! – Naor

1

Es gibt nur eine Möglichkeit, einen Digest-Zyklus zu starten: Aufruf $digest. Es wird selten direkt aufgerufen.

$apply ist im Wesentlichen ein sicherer Wrapper für $digest. Sicher bedeutet, dass es Fehler behandelt. In den meisten Fällen ist dies die Methode, die Sie aufrufen, um Änderungen zu propagieren. Ein weiterer wichtiger Unterschied ist, dass es immer einen Digest-Zyklus im Root-Bereich startet, während $digest für jeden Bereich aufgerufen werden kann und nur diesen Bereich und seine Nachkommen betrifft.

Viele Dienste und Anweisungen lösen einen Digest-Zyklus aus, normalerweise durch Aufruf von $apply. Es ist nur wichtig zu wissen, ob Sie sich selbst anrufen müssen oder ob es bereits vom Service/der Direktive aufgerufen wurde.

Ist safeApply noch sicher?

Ja, weil es immer noch nur einen Weg gibt, einen Digest-Zyklus zu starten.

Welche Alternative haben wir anstelle von safeApply() (wenn wir $ mitten in einem Digest-Zyklus aufrufen)?

Kein Aufruf $apply in der Mitte eines Digest-Zyklus. Ernst. In dieser Situation zu sein ist fast immer ein Designproblem. Es ist besser, dieses Problem zu lösen, anstatt es abzudecken.

Die Methode, die von der Lösung verwendet wird, die $$phase überprüft, ist die einzige Möglichkeit zu wissen, ob ein Digest-Zyklus ausgeführt wird.Wie von Ihnen gezeigt, wird es intern sogar von Angular verwendet. Es reicht, if (!$rootScope.$$phase) zu tun. Aber denken Sie daran, dass es ein Hack ist, der mit einer neuen Version von Angular brechen könnte. BTW: Monkey patchen den obersten Bereich, wie vom Autor vorgeschlagen, würde die Lösung für isolierte Bereiche unbrauchbar machen.

Angulars Art der Datenbindung ist für jemanden, der neu in Angular ist, in der Tat schwer zu verstehen. Aber im Grunde läuft alles darauf hinaus: Damit jede Änderung erkannt/verarbeitet wird, muss jemand $apply (oder $digest) anrufen. Das ist es. Dies soll durch Richtlinien und Dienste geschehen. Es ist eigentlich ziemlich schwer, einen '$ apply in progress' Fehler zu bekommen, wenn Sie alles richtig gemacht haben.