Was ist der Unterschied zwischen $ evalAsync und $ timeout in AngularJS?

180

Ich benutze AngularJS jetzt schon eine Weile und habe festgestellt, dass ab und zu $ timeout verwendet werden muss (scheint normalerweise ein jQuery-Plugin zu initiieren).

Vor kurzem habe ich versucht, den Digest-Zyklus besser und gründlicher zu verstehen, und bin auf die Funktion $ evalAsync gestoßen .

Es scheint, dass diese Funktion ähnliche Ergebnisse liefert $timeout, nur dass Sie sie nicht verzögern. Jedes Mal $timeout, wenn ich es verwendet habe, war es mit einer Verzögerung von 0, also frage ich mich jetzt, ob ich es $evalAsyncstattdessen hätte verwenden sollen .

Gibt es grundlegende Unterschiede zwischen den beiden? Welche Fälle würden Sie übereinander verwenden? Ich möchte ein besseres Gefühl dafür bekommen, wann ich welches verwenden soll.

dnc253
quelle

Antworten:

263

Ich habe kürzlich im Wesentlichen diese Frage hier beantwortet: https://stackoverflow.com/a/17239084/215945 (Diese Antwort enthält Links zu einigen Github-Austauschen mit Misko.)

Zusammenfassen:

  • Wenn Code mit $ evalAsync aus einer Direktive in die Warteschlange gestellt wird , sollte er ausgeführt werden, nachdem das DOM von Angular bearbeitet wurde , aber bevor der Browser gerendert wird
  • Wenn Code mit $ evalAsync von einem Controller in die Warteschlange gestellt wird , sollte er ausgeführt werden, bevor das DOM von Angular bearbeitet wurde (und bevor der Browser rendert) - dies ist selten gewünscht
  • Wenn Code mit $ timeout in die Warteschlange gestellt wird , sollte er ausgeführt werden, nachdem das DOM von Angular bearbeitet wurde und nachdem der Browser gerendert hat (was in einigen Fällen zu Flimmern führen kann).
Mark Rajcok
quelle
15
Vielen Dank für die Erklärung. Eines bin ich mir allerdings nicht sicher. Warum macht es einen Unterschied, wenn Sie $ evalAsync von einem Controller oder einer Direktive aus aufrufen? Die asyncQueue weiß nicht, ob sie von einem Controller oder einer Direktive registriert wurde, sondern stellt sie nur in die Warteschlange des aktuellen Bereichs. Hat es damit zu tun, wann etwas in einem Controller gegen einen Controller läuft? Ich möchte nur diesen Teil verstehen.
dnc253
@ dnc253, ich habe mir den Angular-Code nicht angesehen, daher kenne ich die Antwort auf Ihre (gute) Frage nicht. Hoffentlich kann jemand anderes einen Kommentar abgeben.
Mark Rajcok
15
bedeutet "aus einer Direktive" "aus der Verknüpfungsfunktion einer Direktive"? Oder trifft dies auf das Verhalten zu, wenn es entweder über die Link- oder die Controller-Methode einer Direktive ausgeführt wird?
SimplGy
5
Ja, es ist wirklich unklar, was "von einer Direktive" und "von einem Controller" hier bedeuten
Dorn
1
@MarkRajcok, können Sie hier bitte klarstellen: Wenn Code mit $ evalAsync aus einer Direktive in die Warteschlange gestellt wird, sollte er ausgeführt werden, nachdem das DOM von Angular manipuliert wurde - sollte er ausgeführt werden, nachdem das DOM von dieser Direktive oder von anderen Direktiven manipuliert wurde?
Max Koretskyi
59

Beachten Sie beim Erstellen komplexer Anwendungen, dass sich die Leistung auf Ihre Auswahl auswirkt. Außerdem möchte ich die Antwort von Mark mit weiteren technischen Details vervollständigen:

  • $ timeout (Rückruf) wartet, bis der aktuelle Digest-Zyklus abgeschlossen ist (dh alle Modelle und das DOM werden im Winkel aktualisiert), führt dann seinen Rückruf aus, der sich möglicherweise auf das Winkelmodell auswirkt $apply, und startet dann einen vollständigen Wert im Root-Bereich $ und redigest alles.

  • $ evalAsync (Rückruf) hingegen fügt den Rückruf dem aktuellen oder nächsten Digest-Zyklus hinzu. Das heißt, wenn Sie sich innerhalb eines Digest-Zyklus befinden (zum Beispiel in einer Funktion, die von einer ng-clickDirektive aufgerufen wird ), wartet dies nicht auf irgendetwas, der Code wird sofort ausgeführt. Wenn Sie sich in einem asynchronen Aufruf befinden, z. B. a setTimeout, wird ein neuer Digest-Zyklus ( $apply) ausgelöst.

In Bezug auf die Leistung ist es daher immer besser, aufzurufen $evalAsync, es sei denn, es ist für Sie wichtig, dass die Ansicht vor der Ausführung Ihres Codes auf dem neuesten Stand ist, z. B. wenn Sie Zugriff auf ein DOm-Attribut wie die Elementbreite und dergleichen benötigen.

Wenn Sie weitere Informationen zur Unterscheidung zwischen $ timeout, $ evalAsync, $ Digest, $ Apply wünschen, lade ich Sie ein, meine Antwort auf diese andere Frage zu lesen: https://stackoverflow.com/a/23102223/1501926

Lesen Sie auch unbedingt die Dokumentation :

$ EvalAsync übernimmt keine Garantie dafür, wann der Ausdruck ausgeführt wird, nur Folgendes:

  • Es wird nach der Funktion ausgeführt, die die Auswertung geplant hat (vorzugsweise vor dem DOM-Rendering).
  • Nach der Ausführung des Ausdrucks wird mindestens ein $ Digest-Zyklus ausgeführt.

Hinweis: Wenn diese Funktion außerhalb eines $ Digest-Zyklus aufgerufen wird, wird ein neuer $ Digest-Zyklus geplant . Es wird jedoch empfohlen, immer Code aufzurufen, der das Modell innerhalb eines $ apply-Aufrufs ändert. Dies schließt Code ein, der über $ evalAsync ausgewertet wird.

Floribon
quelle
Können Sie bitte erklären, warum $ timeout erforderlich ist, wenn ich auf ein DOM-Attribut zugreifen muss? Angenommen, ich habe <table width = "{{x}}"> Aktualisiert die Überwachungsfunktion von ng-bind nicht das dom-Attribut im Speicher, ich verstehe, dass es keine Chance gibt, die Ansicht neu zu zeichnen, bis der Digest-Zyklus beendet wird.
Sridhar Chidurala
2
@SridharChidurala Da das DOM (das "HTML") während des Digest-Zyklus aktualisiert wird, müssen Sie warten, bis es ausgeführt wird, bevor Sie die Mofifikationen lesen können. Angular rät jedoch davon ab. Sie sollten xdirekt aus Ihrem Bereich und nicht aus dem DOM lesen , damit Sie nicht auf etwas warten müssen. Außerdem sollten Sie ng-styleCSS besser verwenden als die veraltete widthEigenschaft. Wenn Sie weitere Hilfe benötigen, öffnen Sie bitte eine neue Frage zu StackOverflow.
Floribon