38

The $apply function can run on any scope, including $rootScope.

Are there cases when it makes a difference if I run it on my local scope or if I run it on my $rootScope?

I'm asking because I'd like to create a helper function that wraps a given function in an $apply. To do that I'd always need to pass in a scope, which is A) annoying and B) not easy because I don't necessarily have a local scope.

I'd like to always have my helper function call $apply on the $rootScope, but not if there's some risk in doing that.

Roy Truelove
  • 22,016
  • 18
  • 111
  • 153
  • 3
    At first thought this sounded bad but searching a bit it looks like calling $apply on any scope is going to cause $rootScope.$digest to run anyhow so I don't believe you would suffer any performance loss, but it'd be worth testing some http://stackoverflow.com/questions/12333410/why-scope-apply-calls-rootscope-digest-rather-than-this-digest – shaunhusain Jul 25 '13 at 21:21

4 Answers4

49

Running $apply on any scope always results in a $rootscope.$digest. The only case where it might make a difference is when you provide an expression as an argument to $apply. The expression will be evaluated in the current scope (vs. $rootScope), but afterwards $rootscope.$digest is always called.

The source code is quite clear: rootScope.js

Bottom line: If you call $apply with no arguments, it makes no difference.

ivan.a.bovin
  • 1,133
  • 10
  • 17
Pieter Herroelen
  • 5,977
  • 2
  • 29
  • 37
  • 5
    It makes a difference if the code before the apply with no parameter throws an exception. That exception is not caught by angular's error handling. – Ovidiu Buligan Mar 21 '14 at 07:13
  • 1
    Are you saying there is a difference between `thisThrowsAnError();$scope.$apply()` and `thisThrowsAnError();$rootScope.$apply()` ? – Pieter Herroelen Mar 26 '14 at 15:11
  • 6
    I was reffering to $anyScope.$apply(function () {/*throws error*/}) vs /*thorws error*/;$anyScope.$apply(). The latter one is does not go through error handler of angular – Ovidiu Buligan Mar 27 '14 at 06:59
  • Best practice: use `$timeout` instead of `$rootScope.$apply()` in order to avoid hard-to-debug error "digest already in progress" – jediz Nov 29 '16 at 09:42
15

Another reason for running $apply on the $rootScope instead of $scope typically comes for me when I need to call $apply in a service which will be used by different controllers and therefore different scopes.
In this cases I prefer to inject the $rootScope to the service and call $apply on it without worrying on which scopes the service will be used in the future.

VMAtm
  • 27,943
  • 17
  • 79
  • 125
alextomas
  • 151
  • 1
  • 5
11

Running $digest/$apply on any given scope will visit all other scopes using depth-first traversal:

https://github.com/angular/angular.js/blob/3967f5f7d6c8aa7b41a5352b12f457e2fbaa251a/src/ng/rootScope.js#L550-L558

That means that the only difference is that the $digest will start at whatever $scope it was called on

dherman
  • 2,832
  • 20
  • 23
0

/* What happens with $apply */ 
angular.module('myApp',[]).controller('MessageController', function($scope) {
    
      $scope.getMessage = function() {
        setTimeout(function() {
          $scope.$apply(function() {
            //wrapped this within $apply
            $scope.message = 'Fetched after 3 seconds'; 
            console.log('message:' + $scope.message);
          });
        }, 2000);
      }
      
      $scope.getMessage();
    
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<body ng-app="myApp">
      <div ng-controller="MessageController">
        Delayed Message: {{message}}
      </div>  
    </body>
Lekhraj
  • 455
  • 4
  • 9