2

All:

I got a question about how angularjs data digest monitor the changing of scope data, for example:

 <button ng-click="changename()">{{name}}</button>

And the data change function is like:

            $scope.name = "Change Name";
            $scope.changename = function(){
                $scope.name = "name1";
                $scope.name = "name2";
            }

            $scope.$watch("name", function(newname, oldname){
                if(newname!=oldname){
                    console.log("new: "+newname, "old: "+oldname);
                }
            });

This changename function change scope.name twice in a row, but the watcher can only catch the change from "Change Name" to "name2" ( the console only print "new: name2 old: Change Name")

Could anyone confirm my guess that the digest cycle only start after a function block finish executing?

Thanks

Kuan
  • 11,149
  • 23
  • 93
  • 201
  • For future readers, you can check out the answers here as well: http://stackoverflow.com/questions/23460025/when-digest-cycle-is-called – user203687 Oct 14 '16 at 20:34

3 Answers3

2

I want to correct the answer from rbaghbanli which is misleading.

Indeed, because JavaScript is single threaded, what happen when you click on that button is:

The variable `$scope.name` is set to "name1";
The variable `$scope.name` is set to "name2";
> end of execution stack

Angular runs a $digest cycle
The $watchers are executed
since a watcher value has changed, angular runs another $digest cycle
The $watchers are executed
The watcher have the same value, end of $digest cycle
> end of execution stack

So basically no watcher nor anything will happen when you are within the same function, until the end of every line of this function.

The reason why Angular runs a digest cycle after your changename call is because it is within a ng-click directive, and as with all Angular built-in directive, the code is actually wrapped inside a $scope.$apply call. You could read the ng-click as the following code (even if technically it is not exactly like this):

element.on('click', function() {
  $scope.changename();
  $rootScope.$digest();
});

Note: I wanted to simplify the generic idea, but to be technically true, Angular executes its $digest cycle right after your code (as you can see in my pseudo ng-click), within the same execution stack. Which means any setTimeout or other asynchronous call will happen after the digest cycle and after the DOM has been updated with the latest changes, which can be useful sometimes.

floribon
  • 19,175
  • 5
  • 54
  • 66
  • Yes, your guess was right, and that is not so specific to AngularJS but a Javascript thing. Typically, if you want to execute a code *after* the current "execution stack", we use `setTimeout` – floribon May 14 '15 at 23:56
  • Thanks, could you help with another question: http://stackoverflow.com/questions/30243804/why-ng-repeat-report-10-digest-iterations-reached – Kuan May 15 '15 at 00:00
  • The only change I'd suggest to this is regarding the simplified version, that it's really more like `try { $scope.changename(); } finally { $rootScope.$digest(); }` . You'll get your `$digest` even if there are errors in the function. – DRobinson May 15 '15 at 00:00
  • Right @DRobinson, but I prefer to keep is the simplest for this specific question, and people would find a better explanation elsewhere if they wonder what is a digest cycle or what is the difference between `scope.$digest` and `scope.$apply`. But thanks for the precision – floribon May 15 '15 at 00:02
  • Sure, hence me adding a comment rather than editing your answer. I didn't see a need for your answer to change, but wanted it in the comments in case anybody got the crazy idea of "well if I force it to fail..." – DRobinson May 15 '15 at 00:07
0

Yes and no. JavaScript is executed on single thread, so there will be 2 invocation of $watch function, but only after $scope.changename() completes, so 2nd invocation will have newname == oldname == 'name2'.

Riad Baghbanli
  • 3,105
  • 1
  • 12
  • 20
0

Generally Angular triggers a digest cycle after some event has taken place. In your case, the ngClick directive triggers a digest after the provided expression is executed. The same is true for a number of other user-triggered events, e.g. mouseover, keydown, keypress.

So it doesn't matter how many times you change name inside changename(); the next digest cycle will only "see" the last value assigned.

Michael Benford
  • 14,044
  • 3
  • 60
  • 60
  • Thanks, the key answer I am looking for is when the digest starts, so according to yours, it starts after the ng-click function finished and basically, you confirm my guess, right? – Kuan May 14 '15 at 23:53
  • @Kuan In this particular case, yes. But you don't have to guess this stuff. When in doubt, check Angular's source code. I just did that in order to answer your question. :) – Michael Benford May 14 '15 at 23:57
  • thanks for suggestion, I am pretty entry level. So I guess I am not that into reading source. – Kuan May 14 '15 at 23:58