Madd0g
Madd0g

Reputation: 4001

Too many listeners and slow $compile, need performance tips or something between two-way binding and one-time binding

Sorry, I don't have a live example, I'm looking for performance advice.

I've finally hit my first performance problem with angular, I have a pretty complex UI and in it, I have a directive with about 3 nested repeats that use directives, in every level the directive uses scope: true and using bindToController syntax.

The data source is not that big, but every repeat ends up being between 30-100 watchers (I'm using this snippet to count the watchers)

I'm calling $compile on the top directive and it takes way over a second (!) to show the HTML (it ends up having a few hundred listeners), ng-if and ng-class and other custom directives, it quickly adds up.

I've added one-time bindings wherever I could, but I'm still at way over a 1000 in total and I guess that can slow things down?

I've ran CPU profilers in different browsers, drilled as deep as possible and I never saw my code taking up and significant time, all I see it jquery/angular taking a long long time, but none of the functions inside show any significant self time.

profiler

If I open that 800ms elemData.handle, all I see is angular's $scope.eval, compileDirectives and a bunch of other angular stuff in a deep tree.

Could using scope: true be the culprit? Would complex directives perform better if they use an isolate scopes?

Maybe there are advanced methods I don't know to use one-time bindings? Or something between two-way and one-way?

EDIT: for posterity, this is what happened: I managed to replace my isolate scope directives on the inner-most ng-repeat level with simple ng-includes, the functionality is the same, but execution time is 1/1000. I don't think I'll ever really know why the difference was so huge, I never had many items in the ng-repeat, but the code now runs in 1ms :)

Upvotes: 2

Views: 2230

Answers (1)

Appeiron
Appeiron

Reputation: 1061

  1. Use $destroy events to clean up event watchers
  2. Use one-way-binding everywhere what you can't change manually (ng-repeat, ng-options)
  3. In templates try to avoid functions that will return value. Simple assignment not needed to be compiled but function will run each $digest (ng-if="ctrl.isDataLiading()" slower then ng-if="ctrl.isDataLoaded")
  4. In list items with a lot of ng-repeat avoid filters that will recount and run collection watchers on arrays that is taking great portion of performance - instead use ng-if to remove filtered items

When I was implementing tree structure that can be like expanded to see child nodes (list with + option) I struggled with such problem. Above approach made functionality better but speed did not rise a lot.

In common words all my answer means: angular populates too many watchers, get rid of them

Upvotes: 4

Related Questions