Reputation: 1971
Let's say someone wrote a method like this in a file called app.js
trying to peform an XHR request angainst a non existing url:
app.controller('MainCtrl', function($scope,$http) {
$scope.send = function() {
$http.get('http://run.plnkr.co/thisIs404');
};
});
I can see an error regarding URL http://run.plnkr.co/thisis404
in console and network panel :
To debug this I want to find quickly where this XHR call was made in sources (ie find the app.js
file) :
So I enable in chrome dev tools :
Debugger actually stops on XHR request, but the call stack only displays references to angular.js "core" files : no reference to app.js
anywhere to be found.
I tried this with chromium 36 and chrome 35. Only solution : search for the wrong URL in the whole code base (which in some case may be hard to do).
app.js
somwhere in the stack ?app.js
file easily from the console error ?With vanilla XHR requests (ie without angular), XHR debug call stack displays the XHR call in app.js
(which is easier to debug in this case) :
Full example here : http://plnkr.co/edit/lnCRpv?p=preview
[EDIT] As i've been asked : Angular.js is not minified in my tests.
Upvotes: 18
Views: 9185
Reputation: 4045
One option that came in my mind is to create a module for $http debugging that you can add it as a dependency in your main module whenever you need to debug $http calls. There a decorator for the $http service can be registered that will simply log the arguments of the call a forward it to the $http service. There a breakpoint can be set too.
I have created a simple working example here. I hope it will help.
Example $http logger decorator implementation:
var httpDebugging = angular.module('http-debugging', []);
httpDebugging.decorator('$http', function ($delegate) {
var debugAware = function (fnCallback) {
return function () {
var result = fnCallback.apply(this, arguments);
console.log('$http decorator: ', arguments);
return result;
};
};
for(var prop in $delegate) {
if (angular.isFunction($delegate[prop])) {
$delegate[prop] = debugAware($delegate[prop]);
}
}
return $delegate;
});
Upvotes: 0
Reputation: 60787
So, you see, this issue is mostly because angular's $http sucks. Sorry about that.
Let's try to use the bluebird library, because it provides long stack traces.
Promise.longStackTraces();
Promise.resolve($http.get('...'));
You get the following stack trace:
Possibly unhandled Error: [object Object]
at Promise$_rejectFromThenable (http://cdn.jsdelivr.net/bluebird/1.2.4/bluebird.js:4736:52)
at wrappedErrback (https://code.angularjs.org/1.3.0-beta.5/angular.js:11334:78)
at wrappedErrback (https://code.angularjs.org/1.3.0-beta.5/angular.js:11334:78)
at wrappedErrback (https://code.angularjs.org/1.3.0-beta.5/angular.js:11334:78)
at https://code.angularjs.org/1.3.0-beta.5/angular.js:11467:76
at Scope.$eval (https://code.angularjs.org/1.3.0-beta.5/angular.js:12418:28)
at Scope.$digest (https://code.angularjs.org/1.3.0-beta.5/angular.js:12230:31)
at Scope.$apply (https://code.angularjs.org/1.3.0-beta.5/angular.js:12522:24)
at done (https://code.angularjs.org/1.3.0-beta.5/angular.js:8207:45)
at completeRequest (https://code.angularjs.org/1.3.0-beta.5/angular.js:8412:7)
(Plunker here.)
The most important line is the first: Possibly unhandled Error: [object Object]
.
Yep. An object is thrown, not a real Error
object, with the stack
property attached to it. For the reference, here is how to throw an error and keep its stack along with it: https://github.com/Ralt/newerror/blob/master/index.js
So, how to fix this? It depends on several decisions:
If you want to add a real Promise lib, use bluebird. AFAIK, it is one of the few that provides long stack traces, and it is the fastest one out there.
For a proper xhr lib that throws real errors, I'm afraid you're out of luck there. Writing a custom one with the support for browsers you want isn't really hard though. With no IE8 support, this works (with bluebird):
function xhr(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(xhr.responseText);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
});
}
(Plunker here.)
As you can see, the stack trace is informative:
Upvotes: 11
Reputation: 1971
XHR requests are stacked in $http.pendingRequests
array and are send later. Which is why you can't find a direct linked between where $http
is called and where the actual XHR request is made.
If you want to know which function called $http
you have to set a breakpoint in $http
function.
It kinds of defeat the whole "XHR breakpoints" purpose in my opinion.
Upvotes: 1