web-dev-qa-db-fra.com

Angular $ scope. $ Apply vs $ timeout as a safe $ apply

J'essaie de mieux comprendre les nuances de l'utilisation du service $ timeout dans Angular comme une sorte de méthode "$ sûre". Fondamentalement, dans les scénarios où un morceau de code pourrait s'exécuter en réponse à soit un événement Angular ou un événement non angulaire tel que jQuery ou un événement DOM standard.

Si je comprends bien les choses:

  1. L'encapsulation du code dans $ scope. $ Apply fonctionne très bien pour les scénarios où vous n'êtes pas déjà dans une boucle de résumé (alias. Événement jQuery) mais générera une erreur si un résumé est en cours
  2. L'encapsulation de code dans un appel $ timeout () sans paramètre de délai fonctionne, que ce soit déjà dans un cycle de résumé ou non

En regardant Angular, il semble que $ timeout effectue un appel à $ rootScope. $ Apply ().

  1. Pourquoi $ timeout () ne génère-t-il pas également une erreur si un cycle de résumé est déjà en cours?
  2. La meilleure pratique consiste-t-elle à utiliser $ scope. $ Apply () lorsque vous savez avec certitude qu'un résumé ne sera pas déjà en cours et $ timeout () lorsque vous en avez besoin pour être sûr de toute façon?
  3. $ Timeout () est-il vraiment une "application sûre" acceptable, ou y a-t-il des problèmes?

Merci pour tout aperçu.

68
bingles

En regardant Angular, il semble que $ timeout effectue un appel à $ rootScope. $ Apply ().

  • Pourquoi $ timeout () ne génère-t-il pas également une erreur si un cycle de résumé est déjà en cours?

$timeout Utilise un service non documenté Angular $browser. Spécifiquement, il utilise $browser.defer() qui diffère l'exécution de votre fonction de manière asynchrone via window.setTimeout(fn, delay), qui s'exécutera toujours en dehors de Angular cycle de vie. Une fois que window.setTimeout a déclenché votre fonction, $timeout appellera $rootScope.$apply().

  • La meilleure pratique consiste-t-elle à utiliser $ scope. $ Apply () lorsque vous savez avec certitude qu'un résumé ne sera pas déjà en cours et $ timeout () lorsque vous en avez besoin pour être sûr de toute façon?

Je dirais que oui. Un autre cas d'utilisation est que, parfois, vous devez accéder à une variable $ scope dont vous savez qu'elle ne sera initialisée qu'après le résumé. Un exemple simple serait si vous souhaitez définir l'état d'un formulaire sur sale dans le constructeur de votre contrôleur (pour une raison quelconque). Sans $ timeout, le FormController n'a pas été initialisé et publié sur $ scope, donc le fait d'envelopper $scope.yourform.setDirty() dans $ timeout garantit que FormController a été initialisé. Bien sûr, vous pouvez faire tout cela avec une directive sans $ timeout, en donnant simplement un autre exemple de cas d'utilisation.

  • $ Timeout () est-il vraiment une "application sûre" acceptable, ou y a-t-il des problèmes?

Cela devrait toujours être sûr, mais votre méthode go to devrait toujours viser $ apply () à mon avis. L'actuelle application Angular sur laquelle je travaille est assez grande et nous n'avons eu à compter qu'une seule fois sur $ timeout au lieu de $ apply ().

61
Beyers

Si nous utilisons beaucoup $ apply dans l'application, nous pourrions obtenir le résumé Error: $ déjà en cours. Cela se produit car un cycle $ digest peut être exécuté à la fois. Nous pouvons le résoudre par $ timeout ou par $ evalAsync.

Le $ timeout ne génère pas d'erreur comme "$ digest déjà en cours" car $ timeout indique Angular qu'après le cycle en cours, il y a un timeout en attente et de cette façon il garantit qu'il n'y en aura pas les collisions entre les cycles de résumé et donc la sortie de $ timeout s'exécuteront sur un nouveau cycle de $ digest.

J'ai essayé de les expliquer à: Comparaison de apply, timeout, digest et evalAsync .

Peut-être que cela vous aidera.

13
Rahul Garg

Pour autant que je le comprends, $timeout est un wrapper autour de setTimeout qui appelle implicitement $scope.$apply, ce qui signifie qu'il s'exécute en dehors du cycle de vie angular, mais démarre le cycle de vie angular lui-même. Le seul "problème" auquel je peux penser est que si vous vous attendez que votre résultat soit disponible this$digest, vous devez trouver un autre moyen de "postuler en toute sécurité" (qui, AFAIK, n'est disponible que via $scope.$$phase).

4
Jeff Hubbard