After experimenting with the $provide.decorator() method in AngularJS, I wanted to try looking at another form of decorator (of sorts) - an interceptor. For a long time, AngularJS has allowed us to intercept $http responses. Then, in AngularJS 1.1.4, they extended interceptor functionality to include both outgoing requests and incoming responses. Now, with our ability to straddle both sides of the HTTP fence, I wanted to see if I could use interceptors to track and monitor HTTP requests within an AngularJS application.
Like the $provide.decorator() in my previous blog post, the $http request interceptors have to be setup during the configuration phase of the bootstrapping. The config phase is the only time we have access to the providers before the underlying services have been instantiated.
The entire $http request / response interceptor chain is implement as a Promise chain. The core $http request sits, as a promise, in the middle of the chain. Your request interceptors are then added before the core request while your response interceptors are added after the core request:
It's quite beautiful, isn't it? Using interceptors, we should be able to tally up all the outgoing requests and then decrement pending requests on the way back. In practice, this actually becomes quite easy.
In theory, however, this is problematic. See, in theory, you could have a competing request interceptor or a response interceptor that completely modifies the config object, or throws an error making the config object unavailable. In such - albeit outlier edge-cases - it will become very difficult (in an elegant way) to reconcile an outgoing request with an incoming response.
REALITY CHECK: Now, again, in practice, I want to stress that this will likely never happen. In practice, you probably only have one interceptor (if that) and know exactly what it's doing. The complexity would really only occur in cases where you're using a third-party module that has implemented an $http interceptor that is doing something silly and unfortunate.
That said, I wanted to try to and explore $http interceptors in a way that makes the code a bit more defensive. In the following demo, I have two main blocks - the trafficCop service which manages the tally of HTTP request activity and, the config() block which sets up the interceptor that pipes $http activity into the trafficCop service:
As you can see, the bulk of the code deals with the "what if" situations. What if the request is rejected? What if the response doesn't have a config object? What if the response config method doesn't match a pending method? In reality, none of these "what ifs" would ever occur. But, the mechanism provided by the promise chain means that they "could." And, I thought it would make the exploration more interesting.
And, when we run the above code, we get the following console output:
The more I dig into AngularJS, the more I learn to love Promises. And, with the way that AngularJS wraps $http requests in a promise chain, it gives us an opportunity to track outgoing and incoming HTTP activity.
Want to use code from this post? Check out the license.