5

During profiling of my angularjs application, I found that sometimes $get.Scope.$eval takes more than 100ms. During the single $digest loop there are at least 3 long cases of $get.Scope.$eval and I'd like to optimize this part.

Below the $get.Scope.$eval in profiler I see only invoking of angularjs code.

Here is a screenshot of profile chart.

enter image description here

Could anyone suggest, what's going on and how to optimize this part? I suppose that it can be caused by ng-repeat, ng-include or ng-if, but I'm not sure.

Update: Here is the simplified structure of my application. Probably, the problem is in the architecture of my application. The app mostly working on the single route and change it only for 3 cases, so application store state in the global controller AppController - fat controller. Also there are 20k+ nodes in the html and the number can grow(maximum I saw is 60k)

uladzimir
  • 5,639
  • 6
  • 31
  • 50

1 Answers1

1

$eval is used internally by angular when resolving angular expressions, such as {{variable}}. Without seeing any of your code, it's hard to tell what expressions are using resources unnecessarily, but usually too large or nested ng-repeats (or many ng- directives included within an ng-repeat) are a code smell.

Angular uses dirty checking to evaluate these expressions (for lack of a better option) - that means that every time you create a binding with the {{}} syntax, it creates an implicit $watch expression getting that value, that will be called every digest cycle to see if the value has changed (on which change the relevant parts of the DOM are regenerated).

Here's one optimization I've used successfully in the past:

most of the time, when you bind a value with {{}}, you don't actually expect the value to change (like a label), and this 2-way data binding is completely superflous. Since angular version 1.3 you have the options to create one-time bindings with the :: syntax:

One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value

which eliminates the corresponding performance overhead of such bindings. (If you're using older angular versions, external libraries can mimick this behaviour, such as bindonce.)

Here are some additional tools I've found useful while profiling/optimizing angular apps:

  • Batarang, a chrome extension "for debugging and profiling angular applications"
  • This stackoverflow answer, giving a neat solution for counting how many watch expressions are active on your page. A rule of thumb is that if you're above 2000, you'll start noticing performance issues, and you should think about changing your architecture - employ lazy loading mechanisms, reconsider whether you truly need all the bindings etc.

  • In production environments disabling the default "debug mode" of angular can also help with performance.

Community
  • 1
  • 1
doldt
  • 4,466
  • 3
  • 21
  • 36
  • Thanks! Unfortunately I already use and and `::`, and batarang and trying to reduce a number of watchers. I have less then 2k of watchers and really slow application. Probably architecture that I use is wrong, but it's another question. – uladzimir Jul 29 '15 at 11:47
  • As for architecture, I have a lot of `ng-if`, `ng-show` and `ng-repeat` without the changing the route. And after every user's action all my 300+ scopes need to be digested. And probably `$eval` tries to evaluate a lot of my nested `ng-include`, but I don't know and trying to understand it with my question – uladzimir Jul 29 '15 at 11:49
  • 300+ active scopes sounds like a nightmare, both performance and maintainability-wise. Are you sure you need all of those at the same time? Can you give us some code samples of some more complicated nested structures? – doldt Jul 29 '15 at 11:55
  • sure, but it's probably a more common question that current one. I'll add sheme of code structure a little bit later – uladzimir Jul 29 '15 at 12:10
  • Here is simplified structure of the code https://gist.github.com/havenchyk/c1c668de6bc112ea93f2#file-code-md I change route only for 3 cases, so mostly application store state in the global controller `AppCtrl` – uladzimir Jul 29 '15 at 12:17
  • You should edit these comments into your original question, so people can see them and respond properly. – doldt Jul 29 '15 at 12:45