Angular $scope.$apply vs $timeout als een veilige $apply

Ik probeer de nuances van het gebruik van de $timeout-service in Angular beter te begrijpen als een soort “veilige $apply”-methode. In principe in scenario’s waarin een stuk code kan worden uitgevoerd als reactie op een Angular-gebeurtenis of een niet-hoekige gebeurtenis zoals jQuery of een standaard DOM-gebeurtenis.

Zoals ik het begrijp:

  1. Code inpakken in $scope.$apply werkt prima voor scenario’s waarin u
    zijn nog niet in een digest-lus (ook bekend als jQuery-gebeurtenis) maar geven een foutmelding als er een digest wordt uitgevoerd
  2. Code inpakken in een $timeout()-aanroep zonder vertragingsparameter werkt al dan niet in een samenvattingscyclus

Kijkend naar de Angular-broncode, lijkt het alsof $timeout een aanroep doet naar $rootScope.$apply().

  1. Waarom geeft $timeout() ook geen foutmelding als er al een samenvattingscyclus bezig is?
  2. Is de beste manier om $scope.$apply() te gebruiken als je zeker weet dat er nog geen digest wordt uitgevoerd en $timeout() als het hoe dan ook veilig moet zijn?
  3. Is $timeout() echt een acceptabele “veilige toepassing”, of zijn er valkuilen?

Bedankt voor enig inzicht.


Antwoord 1, autoriteit 100%

Kijkend naar de Angular-broncode, lijkt het alsof $timeout een oproep doet naar
$rootScope.$apply().

  • Waarom geeft $timeout() ook geen foutmelding als er al een samenvattingscyclus bezig is?

$timeoutmaakt gebruik van een ongedocumenteerde Angular-service $browser. Het gebruikt met name $browser.defer()die de uitvoering van uw functie asynchroon uitstelt via window.setTimeout(fn, delay), die altijd buiten de Angular-levenscyclus zal worden uitgevoerd . Pas als window.setTimeoutis geactiveerd, zal uw functie $timeout$rootScope.$apply()aanroepen.

  • Is de beste manier om $scope.$apply() te gebruiken als je zeker weet dat er nog geen digest wordt uitgevoerd en $timeout() als het hoe dan ook veilig moet zijn?

Dat zou ik zeggen. Een ander gebruiksscenario is dat u soms toegang moet hebben tot een $scope-variabele waarvan u weet dat deze pas na digest zal worden geïnitialiseerd. Een eenvoudig voorbeeld zou zijn als u de status van een formulier in uw controllerconstructor wilt instellen op vuil (om welke reden dan ook). Zonder $timeout is de FormControllerniet geïnitialiseerd en gepubliceerd op $scope, dus het inpakken van $scope.yourform.setDirty()in $timeout zorgt ervoor dat FormControlleris geïnitialiseerd. Natuurlijk kun je dit allemaal doen met een richtlijn zonder $time-out, maar geef gewoon een ander gebruiksvoorbeeld.

  • Is $timeout() echt een acceptabele “veilige toepassing”, of zijn er valkuilen?

Het zou altijd veilig moeten zijn, maar je go to-methode moet naar mijn mening altijd streven naar $apply(). De huidige Angular-app waar ik aan werk is vrij groot en we hoefden maar één keer op $timeout te vertrouwen in plaats van $apply().


Antwoord 2, autoriteit 24%

Als we $apply intensief gebruiken in de toepassing, krijgen we mogelijk de foutmelding: $digest is al bezig. Het gebeurt omdat er één $digest-cyclus tegelijk kan worden uitgevoerd. We kunnen het oplossen door $timeout of $evalAsync.

De $timeout genereert geen fout zoals “$digest reeds bezig“ omdat $timeout Angular vertelt dat er na de huidige cyclus een time-out wacht en op deze manier zorgt het ervoor dat er geen botsingen zullen zijn tussen digest-cycli en dus uitvoer van $timeout wordt uitgevoerd in een nieuwe $digest-cyclus.

Ik heb geprobeerd ze uit te leggen op: Vergelijking van toepassen, timeout,digest en evalAsync.

Misschien helpt het je.


Antwoord 3, autoriteit 6%

Voor zover ik het begrijp, is $timeouteen wrapper rond setTimeoutdie impliciet $scope.$applyaanroept, wat betekent dat het wordt uitgevoerd buiten de hoekige levenscyclus, maar start de hoekige levenscyclus zelf. De enige “fout” die ik kan bedenken is dat als je verwacht dat je resultaat dit$digestbeschikbaar zal zijn, je een andere manier moet vinden om “veilig toe te passen” (die, AFAIK, alleen beschikbaar is via $scope.$$phase).

Other episodes