9

Background

angular's $http service is triggering a $digest on every get (if no $digest is already running):

if (!$rootScope.$$phase) $rootScope.$apply();  

Ia addition to fetching objects from our API, our app has many directives with templateUrl - which are fetched by angular using $http. This causes hundreds of $digest loops on cold start.

Commenting out the above line, reduces the number of $digest loops to about 3, and the app runs MUCH faster, with no bindings broken (at least due to $http not triggering a $digest).

Question

Is there a way to disable $http triggering the $digest?

seldary
  • 6,186
  • 4
  • 40
  • 55
  • 1
    Some [interesting thoughts](http://stackoverflow.com/questions/21935215/make-angularjs-skip-running-a-digest-loop-if-http-get-resulted-in-no-new-data) if you haven't checked that already. – Goodzilla Aug 28 '14 at 13:02
  • 2
    Why not skip the HTTP calls altogether? - Use the `$templateCache`. (Or am I mistaking and `$apply` is called even if no AJAX request has to be sent?) – Sergiu Paraschiv Aug 28 '14 at 13:10
  • @Goodzilla - Thanks, but we can't move to WebSockets at the moment, and we hacking the watchers list won't help in this case. – seldary Aug 28 '14 at 13:12
  • @SergiuParaschiv - Thanks, but we already do that. We even store the templates in the localStorage using angular-cache. I still doesn't help in a "cold start" scenario, which is very important to us. – seldary Aug 28 '14 at 13:15
  • Turns out `$templateCache` is implemented at a lower level and the `$apply` is called even if the template is already in the cache. But looking at the sources I see this line: https://github.com/angular/angular.js/blob/master/src/ng/http.js#L984 Is `useApplyAsync` not doing the job? – Sergiu Paraschiv Aug 28 '14 at 13:27
  • Oh...it's a six days old commit: https://github.com/angular/angular.js/commit/ea6fc6e69c2a2aa213c71ed4e917a0d54d064e4c :) But I think this should do the trick in your case. – Sergiu Paraschiv Aug 28 '14 at 13:28
  • @SergiuParaschiv - We use the latest beta.. I guess it is not edgy enough :-) But even if we has that feature, I think it would only aggregate gets in ~10msec batches, which I believe is still not enough. – seldary Aug 28 '14 at 13:35
  • @SergiuParaschiv `done` is only called when the object is not cached and a request is sent: `if (isUndefined(cachedResp)) { $httpBackend(config.method, url, reqData, done` – a better oliver Aug 28 '14 at 13:41
  • Again, at a cold start (nothing is cached), we still have to fetch X templates + Y objects. We try to keep X+Y to a minimum, but it is still very significant. – seldary Aug 28 '14 at 13:47
  • That's not what I meant by using `$templateCache`. I'm talking about this: https://www.npmjs.org/package/grunt-angular-templates _BUT_ the `template` parameter on directives accepts a function. Check this out: http://plnkr.co/edit/ilffq2JkaziAOX6wUGJf?p=preview It completely bypasses `templateUrl` and a single `$apply` is called. Inside it you can get the actual template from localStorage. – Sergiu Paraschiv Aug 28 '14 at 13:51
  • @SergiuParaschiv - Yeah, we don't use node, and still, it solves only the templateUrl issue. We also have many objects fetched from an API, which can't preload... templateUrl is just half of the problem's effects. – seldary Aug 28 '14 at 14:07
  • Then don't use `$http`. – Sergiu Paraschiv Aug 28 '14 at 14:24

2 Answers2

3

use $httpProvider.useApplyAsync(true); on app config. then the templates directive loaded within 10ms will face the same digest. this will reduce the digest cycle call. see here

Fisherman
  • 5,949
  • 1
  • 29
  • 35
0

It may not be possible to do what you want directly, but you can limit the impact of those digest cycles. Try putting a <div ng-if="everythingLoaded"> around most of the page, and set everythingLoaded to true after all of your $http calls complete.

For example:

var p1 = $http.get(...);
var p2 = $http.get(...);
Promises.all([p1, p2]).then(function() {
  $scope.everythingLoaded = true;
});

The digest cycle will still run but will be much faster because the ng-if will eliminate the impact on the DOM and much of your controller until the condition becomes true.

I'm pretty sure using template caching will solve for the template loading problem, assuming it is still a problem in recent angular versions.

Steve Campbell
  • 3,385
  • 1
  • 31
  • 43
  • Just for your information in case you didn't notice, this question was presented more than a year ago. The angular releases have changed *drastically* in the past year, and though your answer may be correct, you might want to review it to ensure what angular versions this would apply to. – Claies Nov 24 '15 at 00:20
  • @Claies this is pretty straightforward and is valid all the way to 1.5.9 (current). Stuff hidden behind an ng-if won't be digested because it doesn't exist in the DOM at the moment a digest is triggered. – SethWhite Jan 03 '17 at 17:41
  • 1
    @SethWhite I'm sure that's the case. This comment is now about a year old comment to a two year old question, and the only point I was trying to make at that time was that answering a year old question is something that should involve more scrutiny, since it is much more likely to be relevant to others than to the original poster. – Claies Jan 04 '17 at 03:15