Stuck with infinitely repeating digest loops - angularjs

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:
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).
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.
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);

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.

Related

How to test applications that use long polling with Protractor

I am building an Angular app that uses long polling to receive fast updates whenever something changes on the server. I use $resource like this to fetch the actual data:
appServices.factory('Data', ['$resource',
function(){
return $resource('', {}, {
query: {"url": …, isArray: false}
});
}]);
Then I have a service that takes care of the long polling: Wait that the data are loaded; store them somewhere; after one second, start the next long-polling cycle:
app.factory(„DataLoader“, [„Data“, "$timeout", function(Data, $timeout) {
return {
loadData: function() {
var parent = this;
var data = Data.query({},
function(result) {
/* do something to the data,
* then start waiting for an update from the server again
*/
$timeout(function() {
parent.loadData();
}, 1000);
}
);
}
};
});
It works like a charm so far.
However, I am now trying to write Protractor tests for this. The problem is: The server times out the long polling requests after 30 seconds only if there are no changes to the data. As I am waiting for new data inside $timeout, Protractor times out before any results arrive.
I have googled the last hour, but there doesn't seem to be a solution except for using $interval instead of $timeout. This works in a good old polling setup (poll every 3 seconds, get empty results from the server if there's nothing new). However, to avoid exactly that, I implemented long polling. $timeout is just the much more sensible option for me.
Can you give me any tips how to get Protractor running successfully in this environment?
I would suggest taking a look at Protractor's documentation on timeouts. You will probably need to increase your allScriptsTimeout in your configuration file since the default wait for page synchronization is 11 seconds.
Use $interval angular api and $fetch for polling data continuously in your app
If you dont want to use $interval instead of $timeout; which is not a good practice,
then you have to turn off the browser synchronization with angular browser.waitForAngularEnabled(false) and use either browser.sleep() or browser.wait() to achieve synchronization between the elements that you interact with on the page.

Find duration of a REST call in Angular.js

I want to find out the time taken by a REST service to send back the promise object. If a REST service takes more than lets say x seconds, I need to show user a spinner and once the promise object is obtained the normal flow should proceed.
Any ideas?
Thanks
Recording the time of the request seems unnecessary.
Why not just always setup a timeout that will trigger the spinner after x seconds.
In the success callback of the promise you can just destroy the timeout object preventing it from triggering the spinner if it's before x seconds. Then remove the spinner if it exists.
var duration = 1000 * 1; //1 sec
var timeout = setTimeout(releaseTheSpinner, duration);
var releaseTheSpinner = function() {
//Make spinner
}
Something.update(data).
success(function {
clearTimeout(timeout);
//kill spinner
})
Using setTimeout should suffice. For example:
$scope.useHttp = function() {
$http.get('path/to/stuff')
.success(function(data) {
hideSpinner();
//do stuff with data
});
setTimeout(showSpinner,1000); //will show the spinner after a second (1000 milliseconds).
};
Have a look at the ngProgress directive or the angular loading bar directive, which place a progress at the top of the page. This creates a general, uniform method of displaying progress. Even if the service responds quickly (which is when you don't want to show a progress), the bar moves very quickly. This isn't a direct answer to your question, but a suggested alternative to added complexity around timing and showing a spinner.

angularjs http.get only works first time

I have a function in my controller that calls an api to retrieve some values:
$scope.Refresh= function(){
$http.get('/get/value')
.success(function(data) {
//some actions
})
.error(function(data) {
//some actions
});
} ;
I want to refresh the values occasionally, so I've done:
setInterval($scope.Refresh, 100000);
I will do in a better way, but now I want to solve this.
but there is a problem:
If, in the controller, I say: $scope.Refresh (to execute the function first time), the controller does nothing.
If I write the same function + setInterval (to test and run it) it works first time (outside the function), but never refresh next times (code function inside), to explain, that execute the function but neither .success nor .error is called.
I have seen the headers with a 304 status (not modified) but the values are modified!!
I tried to disable cache but that did not fix the problem.
I tried to give a random value to the route like: /get/value/(randomNuber) but I get nothing
Where is the problem?
Just running:
$scope.Refresh();
should definitely run the function at least once. If it doesn't something is wrong with your code or with your server route. But you should be getting a console error if that's the case.
For setInterval, you should be using the $interval service that ensures your code is run within the angular loop.
Also, per the documentation, you should explicitly cancel this interval when your controller is destroyed.
var httpInterval = $interval($scope.Refresh, 100000);
$scope.$on('$destroy', function() {
$interval.cancel(httpInterval);
});
I've only had intermittent luck with .success and .error, and I'd like to think that part of it was caching the request. I have very consistent, successful results using .then, as shown:
$scope.Refresh= function(){
var myGet = $http.get('/get/value');
myGet.then(function(data){
//do success things here
}, function(data){
//do error things here
});
};
Other than that, follow the advice that #theJoeBiz gave regarding $interval and you should be fine.

AngularJS $http.post responses activated much faster if periodic $rootScope.$digest in effect

In our very large and quite complex AngularJS application, I noticed (by accident!) that a line like this in my main module setup...
application.run(function($rootScope) {
window.setInterval( () => { $rootScope.$digest(); }, 1000);
});
...has significant positive impact, in the activation time of our $http.post requests.
A small part of our code that deterministically reproduces the behaviour is this:
// In the partial
<button ... ng-click="buttonHandler" ...>
// In the controller
$scope.buttonHandler = function() {
$http.post(....).success( function() { console.log("Activated"); })
}
We associate a button's ng-click handler with invocation of one of our web services.
The web service itself responds within 30ms (according to Chrome developer tools).
However, the code inside the .success handler is executed after 1.75 - 2.3 seconds (and only then is the message "Activated" displayed in the console).
However, when we put the Eternal rootScope $digest Hack (TM) in place, the activation occurs in less than a second! :-)
I can only guess that the rootScope $digest somehow 'triggers' the actual invocation of the .success handler, and since the webservice itself responds in 30ms, the activation time depends on when the button press happens to fall in the 1 second period of the hack's setInterval.
I am not saying that this is a recommended practise - but i was very surprised to see it happen. Note that there are no console errors logged or any other mischief reported anywhere in my assertive checks - the code works fine with or without the hack, but with significantly improved performance when the hack is in place.
Any idea what is going on?
Promises in Angular queue up a callback with $rootScope.$evalAsync(callback).
The callback checks to see whether the promise has been resolved/rejected.
$evalAsync schedules the task to run after the next digest cycle is complete.
Therefore, if you're preemptively asking for a digest cycle, your promise may be resolved quicker.
Source: https://github.com/angular/angular.js/blob/master/src/ng/q.js#L175

AngularJS - resource - doas a request have to be inside $scope.$apply?

I could not find anything in the docs about this, but it seems that any request has to be inside a $apply() call - (wether this $apply() call comes from an action or is invoked manually).
I can not explain this strange behavior any other way:
// inside a controller
$scope.resources = Resource.query();
// a request gets sent
works just fine, but
// somewhere else - in a callback for auto-complete
// just to show that this is outside $scope.$apply() - not realy setTimeout
setTimeout(function(){
$scope.resources = Resource.query();
},100);
// no request gets sent;
});
I think you are looking at this issue: https://github.com/angular/angular.js/issues/2371.
You may want to follow up.

Resources