angularjsangularjs-digest

Stuck with infinitely repeating digest loops


I have an Angular SPA I'm running with Angular Loading Bar and AngularJS v1.4.9.

For some time now, it has been so happening that after the app gets loaded, the bar has been getting stuck after a while, indicating that not all requests are done with. Additionally, one of the console.log()s I have in our code have been firing continuously, around 1-2 times every second. The bar completes and the console.log works normally when the user reloads the page(but doesn't stop on its own).

The console.log() is set inside a function attached to a ng-disabled directive, so I know it's an indicator of a digest cycle in progress.

I use Chrome as my browser and I recently did a profiling run. Here's some screenshots of what I see: Bird's eye view

This is a broad view. As is shown here, it's first happening at 100ms, then at 400, then at 600, and so on(I did a 3s run).

strip 1 This is the very first vertical strip. Not all of them look exactly the same as this one, but the completeOutstandingRequest, timeout and Browser.self.defer methods are always there. The searchDisable and log methods are ours, the log is the one I'm talking about above. self.url Here's another one for comparison, but this is slightly different - it has another Browser method: self.url. I'm not sure what it does.

Here are some issues I found which could be related:

Timeout callback can occur in the middle of a digest cycle in Firefox
$browser.defer ends up triggering changeDetection in zonejs through settimeout

P.S. I think this issue first started when we added some interceptors to our code to do some automatic redirects - e.g. when the session has timed out and the user clicks on something, he's automatically returned to the login page to relogin.

This is the interceptor:

        interceptor.$inject = ['$rootScope', '$q'];
        function interceptor($rootScope, $q) {
            return {
                responseError: function (rejection) {
                    var config = rejection.config || {};
                    if (!config.ignoreAuthModule) {
                        switch (rejection.status) {
                            case 401:
                                var deferred = $q.defer();
                                $rootScope.$broadcast('event:auth-loginRequired', rejection);
                                return deferred.promise;
                            case 403:
                                $rootScope.$broadcast('event:auth-forbidden', rejection);
                                break;
                        }
                    }
                    return $q.reject(rejection);
                }
            };
        }

        $httpProvider.interceptors.push(interceptor);

Solution

  • This issue has been solved.

    It turns out that in case of any Unauthorized requests, the promise that is returned on line 11 above was remaining pending forever, because there was no call to .resolve() or .reject() in the deferred object.

    As a result, the loading bar's interceptor was blocked.

    I ended up removing the custom promise entirely and that solved the problem.