Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging Angular's digest cycle: How to find the cause of an infinite loop?

I currently am working on some code with lazy-loaded content + controllers. My code basically works like this fiddle. However, for some reason, my version does not work, and instead I get an infinite digest cycle whenever angular tries to update it's view.

The issue disappears when I remove ng-include from this simple repeat statement:

<div class="container" ng-repeat="pageName in pageNames">
  <div ng-include="pageName"></div>
</div>

Weirdest part: The exact same error occurs even if pageNames is never assigned to the scope. Both scopes (the scope of outer and inner controller - I have each one) can be completely empty (I checked with Batarang - I only have two empty scopes), and I still get the error.

My code is a bit too involved, with too many other dependencies, so posting it here makes no sense. It's purest version is the fiddle above. I cannot find a difference in the logic between the two. When I inspect my scopes with Batarang, I don't see anything suspicious either:

  • I am not using functions in my scopes
  • I did not make use of $watch
  • I did not make use of ng-model

I conclude that I am not explicitly changing anything, so it must be something under the hood of angular.

Can I somehow get Angular or Batarang to tell me which scope variables have changed after a digest iteration, so I can identify the culprit causing the infinite loop?

UPDATE:

I finally figured out that history.pushState messes everything up. I am now looking into alternatives, such as the $location service. Nevertheless, I would still like to know how to debug this kind of issue in general. Any hints?

like image 690
Domi Avatar asked May 21 '14 09:05

Domi


People also ask

How do you debug an infinite loop?

The best first step for debugging an infinite loop is to comment out different sections or lines of code, then running the program to see where the infinite loop is occurring. Start by testing any sections that contain for or while loops, then if/else statements, then other blocks of code.

What triggers digest cycle in Angularjs?

Digest cycle is what Angular JS triggers when a value in the model or view is changed. The cycle sets off the watchers which then match the value of model and view to the newest value. Digest cycle automatically runs when the code encounters a directive.

What is Digest loop in Angularjs?

The digest loop is responsible to update DOM elements with the changes made to the model as well as executing any registered watcher functions. The $digest loop is fired when the browser receives an event that can be managed by the angular context. This loop is made up of two smaller loops.


1 Answers

There's no direct way to get a list of scope variables that changed in each digest, but it's really simple to modify Angular's source to do that (temporarily) if you're in an emergency-debugging situation. The $digest code is here:

angular.js > src > ng > rootScope.js

If you look at this method you'll see references to a watchLog array. AngularJS uses this to report useful error messages when it detects looping like you're getting. You could easily add a few console.log items here, or maintain your own array that you could log later or inspect with your debugger. You can also set breakpoints here to see what is going on.

It's not clear to me why history.pushState() would have anything to do with this because your original question doesn't talk about the use of $location, history, or related functions. However, note that it is a VERY common problem with ngInclude to create infinite loops when paths aren't what you expect. A huge number of server-side (e.g. NodeJS) projects are configured to return the home/index page when a resource isn't found because they assume there's front-end routing going on. That's actually a huge problem because you end up embedding your home page in your home page, which then embeds another copy, etc. I see this kind of problem posted about once a week here on S/O.

A simple test for this is to alter your server to return an empty 404 for any resource not found. I always recommend this whenever somebody mentions "infinite loop" and any kind of asset loading operation: ui-router, ngRoute, ngInclude, etc.

This is particularly common when using relative URLs in paths.

like image 167
Chad Robinson Avatar answered Oct 11 '22 05:10

Chad Robinson