AngularJS : Prevent error $digest already in progress when calling $scope.$apply()

From a recent discussion with the Angular guys on this very topic: For future-proofing reasons, you should not use $$phase

When pressed for the "right" way to do it, the answer is currently

$timeout(function() {
  // anything you want can go here and will safely be run on the next digest.
})

I recently ran into this when writing angular services to wrap the facebook, google, and twitter APIs which, to varying degrees, have callbacks handed in.

Here's an example from within a service. (For the sake of brevity, the rest of the service -- that set up variables, injected $timeout etc. -- has been left off.)

window.gapi.client.load('oauth2', 'v2', function() {
    var request = window.gapi.client.oauth2.userinfo.get();
    request.execute(function(response) {
        // This happens outside of angular land, so wrap it in a timeout 
        // with an implied apply and blammo, we're in action.
        $timeout(function() {
            if(typeof(response['error']) !== 'undefined'){
                // If the google api sent us an error, reject the promise.
                deferred.reject(response);
            }else{
                // Resolve the promise with the whole response if ok.
                deferred.resolve(response);
            }
        });
    });
});

Note that the delay argument for $timeout is optional and will default to 0 if left unset ($timeout calls $browser.defer which defaults to 0 if delay isn't set)

A little non-intuitive, but that's the answer from the guys writing Angular, so it's good enough for me!


Don't use this pattern - This will end up causing more errors than it solves. Even though you think it fixed something, it didn't.

You can check if a $digest is already in progress by checking $scope.$$phase.

if(!$scope.$$phase) {
  //$digest or $apply
}

$scope.$$phase will return "$digest" or "$apply" if a $digest or $apply is in progress. I believe the difference between these states is that $digest will process the watches of the current scope and its children, and $apply will process the watchers of all scopes.

To @dnc253's point, if you find yourself calling $digest or $apply frequently, you may be doing it wrong. I generally find I need to digest when I need to update the scope's state as a result of a DOM event firing outside the reach of Angular. For example, when a twitter bootstrap modal becomes hidden. Sometimes the DOM event fires when a $digest is in progress, sometimes not. That's why I use this check.

I would love to know a better way if anyone knows one.


From comments: by @anddoutoi

angular.js Anti Patterns

  1. Don't do if (!$scope.$$phase) $scope.$apply(), it means your $scope.$apply() isn't high enough in the call stack.

The digest cycle is a synchronous call. It won't yield control to the browser's event loop until it is done. There are a few ways to deal with this. The easiest way to deal with this is to use the built in $timeout, and a second way is if you are using underscore or lodash (and you should be), call the following:

$timeout(function(){
    //any code in here will automatically have an apply run afterwards
});

or if you have lodash:

_.defer(function(){$scope.$apply();});

We tried several workarounds, and we hated injecting $rootScope into all of our controllers, directives, and even some factories. So, the $timeout and _.defer have been our favorite so far. These methods successfully tell angular to wait until the next animation loop, which will guarantee that the current scope.$apply is over.


Many of the answers here contain good advices but can also lead to confusion. Simply using $timeout is not the best nor the right solution. Also, be sure to read that if you are concerned by performances or scalability.

Things you should know

  • $$phase is private to the framework and there are good reasons for that.

  • $timeout(callback) will wait until the current digest cycle (if any) is done, then execute the callback, then run at the end a full $apply.

  • $timeout(callback, delay, false) will do the same (with an optional delay before executing the callback), but will not fire an $apply (third argument) which saves performances if you didn't modify your Angular model ($scope).

  • $scope.$apply(callback) invokes, among other things, $rootScope.$digest, which means it will redigest the root scope of the application and all of its children, even if you're within an isolated scope.

  • $scope.$digest() will simply sync its model to the view, but will not digest its parents scope, which can save a lot of performances when working on an isolated part of your HTML with an isolated scope (from a directive mostly). $digest does not take a callback: you execute the code, then digest.

  • $scope.$evalAsync(callback) has been introduced with angularjs 1.2, and will probably solve most of your troubles. Please refer to the last paragraph to learn more about it.

  • if you get the $digest already in progress error, then your architecture is wrong: either you don't need to redigest your scope, or you should not be in charge of that (see below).

How to structure your code

When you get that error, you're trying to digest your scope while it's already in progress: since you don't know the state of your scope at that point, you're not in charge of dealing with its digestion.

function editModel() {
  $scope.someVar = someVal;
  /* Do not apply your scope here since we don't know if that
     function is called synchronously from Angular or from an
     asynchronous code */
}

// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
  // No need to digest
  editModel();
}

// Any kind of asynchronous code, for instance a server request
callServer(function() {
  /* That code is not watched nor digested by Angular, thus we
     can safely $apply it */
  $scope.$apply(editModel);
});

And if you know what you're doing and working on an isolated small directive while part of a big Angular application, you could prefer $digest instead over $apply to save performances.

Update since Angularjs 1.2

A new, powerful method has been added to any $scope: $evalAsync. Basically, it will execute its callback within the current digest cycle if one is occurring, otherwise a new digest cycle will start executing the callback.

That is still not as good as a $scope.$digest if you really know that you only need to synchronize an isolated part of your HTML (since a new $apply will be triggered if none is in progress), but this is the best solution when you are executing a function which you cannot know it if will be executed synchronously or not, for instance after fetching a resource potentially cached: sometimes this will require an async call to a server, otherwise the resource will be locally fetched synchronously.

In these cases and all the others where you had a !$scope.$$phase, be sure to use $scope.$evalAsync( callback )


Handy little helper method to keep this process DRY:

function safeApply(scope, fn) {
    (scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}