diff --git a/lib/axios.js b/lib/axios.js index 2a6a2e5411a8f57bc0bc7a7ca8d603f82890463d..9ab8445ac83817f169bcca27f693d919d320aaf0 100644 --- a/lib/axios.js +++ b/lib/axios.js @@ -1,6 +1,7 @@ var Promise = require('es6-promise').Promise; var defaults = require('./defaults'); var utils = require('./utils'); +var InterceptorManager = require('./helpers/InterceptorManager'); var axios = module.exports = function axios(config) { config = utils.merge({ @@ -13,7 +14,7 @@ var axios = module.exports = function axios(config) { // Don't allow overriding defaults.withCredentials config.withCredentials = config.withCredentials || defaults.withCredentials; - var serverRequest = function (config) { + function dispatchRequest(config) { return new Promise(function (resolve, reject) { try { // For browsers use XHR adapter @@ -41,24 +42,22 @@ var axios = module.exports = function axios(config) { console.warn('For more information about usage see ' + docs); } } catch (e) {} - } + }; - var chain = [serverRequest, undefined]; + // Hook up interceptors middleware + var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); - utils.forEach(axios.interceptors.request.handlers, function (interceptor) { - chain.unshift(interceptor.request, interceptor.requestError); + axios.interceptors.request.forEach(function (interceptor) { + chain.unshift(interceptor.fulfilled, interceptor.rejected); }); - utils.forEach(axios.interceptors.response.handlers, function (interceptor) { - chain.push(interceptor.response, interceptor.responseError); + axios.interceptors.response.forEach(function (interceptor) { + chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { - var thenFn = chain.shift(); - var rejectFn = chain.shift(); - - promise = promise.then(thenFn, rejectFn); + promise = promise.then(chain.shift(), chain.shift()); } // Provide alias for success @@ -93,20 +92,10 @@ axios.all = function (promises) { }; axios.spread = require('./helpers/spread'); -// interceptors +// Expose interceptors axios.interceptors = { - request: { - handlers: [], - use: function (thenFn, rejectFn) { - axios.interceptors.request.handlers.push({ request: thenFn, requestError: rejectFn }); - } - }, - response: { - handlers: [], - use: function (thenFn, rejectFn) { - axios.interceptors.response.handlers.push({ response: thenFn, responseError: rejectFn }); - } - } + request: new InterceptorManager(), + response: new InterceptorManager() }; // Provide aliases for supported request methods diff --git a/lib/helpers/InterceptorManager.js b/lib/helpers/InterceptorManager.js new file mode 100644 index 0000000000000000000000000000000000000000..15233caf4fc6ec08045187000426534f5e201b12 --- /dev/null +++ b/lib/helpers/InterceptorManager.js @@ -0,0 +1,51 @@ +var utils = require('../utils'); + +function InterceptorManager() { + this.handlers = []; +}; + +/** + * Add a new interceptor to the stack + * + * @param {Function} fulfilled The function to handle `then` for a `Promise` + * @param {Function} rejected The function to handle `reject` for a `Promise` + * + * @return {Number} An ID used to remove interceptor later + */ +InterceptorManager.prototype.use = function (fulfilled, rejected) { + this.handlers.push({ + fulfilled: fulfilled, + rejected: rejected + }); + return this.handlers.length - 1; +}; + +/** + * Remove an interceptor from the stack + * + * @param {Number} id The ID that was returned by `use` + */ +InterceptorManager.prototype.eject = function (id) { + if (this.handlers[id]) { + this.handlers[id] = null; + } +}; + +/** + * Iterate over all the registered interceptors + * + * This method is particularly useful for skipping over any + * interceptors that may have become `null` calling `remove`. + * + * @param {Function} fn The function to call for each interceptor + */ +InterceptorManager.prototype.forEach = function (fn) { + utils.forEach(this.handlers, function (h) { + if (h !== null) { + fn(h); + } + }); +}; + +module.exports = InterceptorManager; + diff --git a/test/specs/interceptors.spec.js b/test/specs/interceptors.spec.js index 5cb04278aa8182a685f6b24b8ca3ddd1746fcac5..cdba90602ca3301939bc278b66bb32b75942f99d 100644 --- a/test/specs/interceptors.spec.js +++ b/test/specs/interceptors.spec.js @@ -296,4 +296,50 @@ describe('interceptors', function () { expect(response.data).toBe('OK123'); }); }); + + it('should allow removing interceptors', function () { + var request, response, intercept; + + runs(function () { + axios.interceptors.response.use(function (data) { + data.data = data.data + '1'; + return data; + }); + intercept = axios.interceptors.response.use(function (data) { + data.data = data.data + '2'; + return data; + }); + axios.interceptors.response.use(function (data) { + data.data = data.data + '3'; + return data; + }); + + axios.interceptors.response.eject(intercept); + + axios({ + url: '/foo' + }).then(function (data) { + response = data; + }); + }); + + waitsFor(function () { + return request = jasmine.Ajax.requests.mostRecent(); + }, 'waiting for the request', 100); + + runs(function () { + request.response({ + status: 200, + responseText: 'OK' + }); + }); + + waitsFor(function () { + return response; + }, 'waiting for the response', 100); + + runs(function () { + expect(response.data).toBe('OK13'); + }); + }); });