Quelle est la différence entre $ evalAsync et $ timeout dans AngularJS?

180

J'utilise AngularJS depuis un petit moment maintenant et j'ai trouvé le besoin d'utiliser $ timeout de temps en temps (il semble généralement que ce soit pour lancer un plugin jQuery).

Récemment, j'ai essayé d'obtenir une compréhension meilleure et plus approfondie du cycle de digestion, et je suis tombé sur la fonction $ evalAsync .

Il semble que cette fonction produit des résultats similaires à $timeout, mais vous ne lui donnez pas de retard. Chaque fois que je l'ai utilisé, $timeoutc'était avec un retard de 0, alors maintenant je me demande si j'aurais dû l'utiliser à la $evalAsyncplace.

Y a-t-il des différences fondamentales entre les deux? Quels cas utiliseriez-vous l'un sur l'autre? J'aimerais avoir une meilleure idée de quand utiliser lequel.

dnc253
la source

Réponses:

263

J'ai récemment répondu essentiellement à cette question ici: https://stackoverflow.com/a/17239084/215945 (Cette réponse renvoie à certains échanges github avec Misko.)

Résumer:

  • si le code est mis en file d'attente à l'aide de $ evalAsync à partir d'une directive , il devrait s'exécuter après que le DOM ait été manipulé par Angular, mais avant que le navigateur ne rende
  • si le code est mis en file d'attente à l'aide de $ evalAsync à partir d'un contrôleur , il doit s'exécuter avant que le DOM n'ait été manipulé par Angular (et avant que le navigateur ne rende) - vous le souhaitez rarement
  • si le code est mis en file d'attente à l'aide de $ timeout , il devrait s'exécuter après que le DOM a été manipulé par Angular, et après le rendu du navigateur (ce qui peut provoquer un scintillement dans certains cas)
Mark Rajcok
la source
15
Merci pour l'explication. Une chose que je ne suis pas sûr de comprendre cependant. Pourquoi cela fait-il une différence si vous appelez $ evalAsync depuis un contrôleur ou une directive? Le asyncQueue ne sait pas s'il a été enregistré à partir d'un contrôleur ou d'une directive, il le met simplement en file d'attente sur la portée actuelle. Est-ce que cela a à voir avec le moment où les choses s'exécutent dans un contrôleur par rapport à un contrôleur? Je veux juste comprendre cette partie.
dnc253
@ dnc253, je n'ai pas regardé le code angulaire, donc je ne connais pas la réponse à votre (bonne) question. J'espère que quelqu'un d'autre pourra commenter.
Mark Rajcok
15
"à partir d'une directive" signifie "à partir de la fonction de liaison d'une directive"? Ou est-ce vrai du comportement lorsqu'il est exécuté à partir de la méthode de liaison ou de contrôleur d'une directive?
SimplGy
5
oui, on ne sait vraiment pas ce que signifient ici "d'une directive" et "d'un contrôleur"
thorn̈
1
@MarkRajcok, pouvez-vous clarifier ici: si le code est mis en file d'attente en utilisant $ evalAsync à partir d'une directive, il devrait s'exécuter après que le DOM a été manipulé par Angular - devrait-il s'exécuter après que le DOM a été manipulé par cette directive ou par d'autres directives?
Max Koretskyi
59

Pour ceux qui créent des applications complexes, sachez que votre choix a un impact sur les performances. Je voudrais également compléter la réponse de Mark avec plus de détails techniques:

  • $ timeout (rappel) attendra que le cycle de digest en cours soit terminé (ie mise à jour angulaire de tout le modèle et du DOM), puis il exécutera son callback - affectant potentiellement le modèle angulaire - puis lancera un full $applysur la racine $ scope, et redigest tout.

  • $ evalAsync (callback) , d'autre part, ajoutera le rappel au cycle de résumé actuel ou suivant. Ce qui signifie que si vous êtes dans un cycle de digest (par exemple dans une fonction appelée à partir d'une ng-clickdirective), cela n'attendra rien, le code sera exécuté tout de suite. Si vous êtes dans un appel asynchrone, par exemple a setTimeout, un nouveau cycle de résumé ( $apply) sera déclenché.

Donc, en termes de performances, il est toujours préférable d'appeler $evalAsync, sauf s'il est important pour vous que la vue soit à jour avant d'exécuter votre code, par exemple si vous avez besoin d'accéder à un attribut DOm tel que la largeur des éléments, etc.

Si vous souhaitez plus de détails sur la distinction entre $ timeout, $ evalAsync, $ digest, $ apply, je vous invite à lire ma réponse sur cette autre question: https://stackoverflow.com/a/23102223/1501926

Assurez-vous également de lire la documentation :

Le $ evalAsync ne donne aucune garantie quant au moment où l'expression sera exécutée, seulement que:

  • il s'exécutera après la fonction qui a programmé l'évaluation (de préférence avant le rendu DOM).
  • au moins un cycle $ digest sera exécuté après l'exécution de l'expression.

Remarque: si cette fonction est appelée en dehors d'un cycle $ digest, un nouveau cycle $ digest sera planifié . Cependant, il est recommandé de toujours appeler le code qui change le modèle à partir d'un appel $ apply. Cela inclut le code évalué via $ evalAsync.

floribon
la source
Pouvez-vous expliquer pourquoi $ timeout est nécessaire si j'ai besoin d'accéder à un attribut DOM. Disons que si j'ai <table width = "{{x}}"> La fonction watch de ng-bind ne met pas à jour l'attribut dom dans la mémoire, je comprends qu'elle n'aura pas la possibilité de repeindre la vue jusqu'à la fin du cycle de résumé.
Sridhar Chidurala
2
@SridharChidurala parce que le DOM (le "HTML") est mis à jour pendant le cycle de résumé, vous devez attendre que cela soit fait avant de pouvoir lire les modifications. Cependant, cela est découragé par Angular, vous devriez lire xdirectement depuis votre scope plutôt que depuis le DOM, pour ne pas avoir à attendre quoi que ce soit. En outre, vous devriez mieux utiliser ng-styleavec css plutôt qu'avec la widthpropriété obsolète . Si vous avez besoin d'aide supplémentaire, veuillez ouvrir une nouvelle question sur StackOverflow.
floribon