Viele der Antworten hier enthalten gute Ratschläge, können aber auch zu Verwirrung führen. Einfach verwenden $timeout
est pas weder die beste noch die richtige Lösung. Lesen Sie das auch, wenn Sie sich Sorgen um die Leistung oder Skalierbarkeit machen.
Was Sie wissen sollten
-
$$phase
ist dem Rahmen vorbehalten, und dafür gibt es gute Gründe.
-
$timeout(callback)
wartet, bis der aktuelle Digest-Zyklus (falls vorhanden) abgeschlossen ist, führt dann den Callback aus und führt am Ende einen vollständigen $apply
.
-
$timeout(callback, delay, false)
tut dasselbe (mit einer optionalen Verzögerung vor der Ausführung des Rückrufs), löst aber keine $apply
(drittes Argument), das die Leistung speichert, wenn Sie Ihr Angular-Modell ($scope) nicht geändert haben.
-
$scope.$apply(callback)
ruft u.a. auf, $rootScope.$digest
Das bedeutet, dass der Wurzelbereich der Anwendung und alle untergeordneten Bereiche neu sortiert werden, auch wenn Sie sich in einem isolierten Bereich befinden.
-
$scope.$digest()
wird einfach sein Modell mit dem View synchronisieren, aber nicht seinen Elternbereich verdauen, was eine Menge Leistung sparen kann, wenn man an einem isolierten Teil des HTML mit einem isolierten Bereich arbeitet (meistens von einer Direktive). $digest nimmt keinen Callback: Sie führen den Code aus und verdauen ihn dann.
-
$scope.$evalAsync(callback)
wurde mit angularjs 1.2 eingeführt und wird wahrscheinlich die meisten Ihrer Probleme lösen. Bitte lesen Sie den letzten Abschnitt, um mehr darüber zu erfahren.
-
wenn Sie die $digest already in progress error
dann ist Ihre Architektur falsch: entweder brauchen Sie Ihren Geltungsbereich nicht neu zu ordnen, oder Sie sollten dafür nicht zuständig sein (siehe unten).
Wie Sie Ihren Code strukturieren
Wenn Sie diesen Fehler erhalten, versuchen Sie, Ihren Bereich zu verdauen, während er bereits in Bearbeitung ist: Da Sie den Zustand Ihres Bereichs zu diesem Zeitpunkt nicht kennen, sind Sie nicht für seine Verdauung zuständig.
function editModel() {
$scope.someVar = someVal;
/* Do not apply your scope here since we don't know if that
function is called synchronously from Angular or from an
asynchronous code */
}
// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
// No need to digest
editModel();
}
// Any kind of asynchronous code, for instance a server request
callServer(function() {
/* That code is not watched nor digested by Angular, thus we
can safely $apply it */
$scope.$apply(editModel);
});
Und wenn Sie wissen, was Sie tun und an einer isolierten kleinen Direktive als Teil einer großen Angular-Anwendung arbeiten, können Sie $digest gegenüber $apply bevorzugen, um Leistung zu sparen.
Aktualisierung seit Angularjs 1.2
Eine neue, leistungsfähige Methode wurde zu jedem $scope hinzugefügt: $evalAsync
. Grundsätzlich führt er seinen Callback innerhalb des aktuellen Digest-Zyklus aus, sofern ein solcher stattfindet, andernfalls beginnt ein neuer Digest-Zyklus mit der Ausführung des Callbacks.
Das ist immer noch nicht so gut wie ein $scope.$digest
wenn Sie wirklich wissen, dass Sie nur einen isolierten Teil Ihrer HTML-Datei synchronisieren müssen (da eine neue $apply
ausgelöst wird, wenn gerade keine Funktion ausgeführt wird), aber dies ist die beste Lösung, wenn Sie eine Funktion ausführen, die Sie können nicht wissen, ob es synchron ausgeführt wird oder nicht z. B. nach dem Abrufen einer Ressource, die möglicherweise im Cache gespeichert ist: Manchmal erfordert dies einen asynchronen Aufruf an einen Server, andernfalls wird die Ressource lokal synchron abgerufen.
In diesen und allen anderen Fällen, in denen Sie eine !$scope.$$phase
verwenden, müssen Sie $scope.$evalAsync( callback )