2
0
mirror of https://github.com/tenrok/axios.git synced 2026-06-02 16:04:10 +03:00

Adding cancellation support

This commit is contained in:
Nick Uraltsev
2016-09-17 11:52:56 -07:00
parent df50698d5a
commit 72dd897bb5
7 changed files with 139 additions and 10 deletions
+9
View File
@@ -185,6 +185,15 @@ module.exports = function httpAdapter(config) {
}, config.timeout);
}
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
req.abort();
reject(cancel);
aborted = true;
});
}
// Send the request
if (utils.isStream(data)) {
data.pipe(req);
+9
View File
@@ -153,6 +153,15 @@ module.exports = function xhrAdapter(config) {
request.upload.addEventListener('progress', config.onUploadProgress);
}
if (config.cancelToken) {
// Handle cancellation
config.cancelToken.promise.then(function onCanceled(cancel) {
request.abort();
reject(cancel);
// Clean up request
request = null;
});
}
if (requestData === undefined) {
requestData = null;
+4
View File
@@ -34,6 +34,10 @@ axios.create = function create(defaultConfig) {
return createInstance(defaultConfig);
};
// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
+27 -9
View File
@@ -2,6 +2,16 @@
var utils = require('./../utils');
var transformData = require('./transformData');
var Cancel = require('../cancel/Cancel');
/**
* Throws a `Cancel` if cancellation has been requested.
*/
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
/**
* Dispatch a request to the server using whichever adapter
@@ -11,6 +21,8 @@ var transformData = require('./transformData');
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// Ensure headers exist
config.headers = config.headers || {};
@@ -52,6 +64,8 @@ module.exports = function dispatchRequest(config) {
// Wrap synchronous adapter errors and pass configuration
.then(adapter)
.then(function onFulfilled(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData(
response.data,
@@ -60,16 +74,20 @@ module.exports = function dispatchRequest(config) {
);
return response;
}, function onRejected(error) {
// Transform response data
if (error && error.response) {
error.response.data = transformData(
error.response.data,
error.response.headers,
config.transformResponse
);
}, function onRejected(reason) {
if (!(reason instanceof Cancel)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(error);
return Promise.reject(reason);
});
};
+66
View File
@@ -0,0 +1,66 @@
var Cancel = axios.Cancel;
var CancelToken = axios.CancelToken;
describe('cancel', function() {
beforeEach(function() {
jasmine.Ajax.install();
});
afterEach(function() {
jasmine.Ajax.uninstall();
});
describe('when called before sending request', function() {
it('rejects Promise with a Cancel object', function (done) {
var source = CancelToken.source();
source.cancel('Operation has been canceled.');
axios.get('/foo', {
cancelToken: source.token
}).catch(function (thrown) {
expect(thrown).toEqual(jasmine.any(Cancel));
expect(thrown.message).toBe('Operation has been canceled.');
done();
});
});
});
describe('when called after request has been sent', function() {
it('rejects Promise with a Cancel object', function (done) {
var source = CancelToken.source();
axios.get('/foo/bar', {
cancelToken: source.token
}).catch(function (thrown) {
expect(thrown).toEqual(jasmine.any(Cancel));
expect(thrown.message).toBe('Operation has been canceled.');
done();
});
getAjaxRequest().then(function (request) {
// call cancel() when the request has been sent, but a response has not been received
source.cancel('Operation has been canceled.');
request.respondWith({
status: 200,
responseText: 'OK'
});
});
});
it('calls abort on request object', function (done) {
var source = CancelToken.source();
var request;
axios.get('/foo/bar', {
cancelToken: source.token
}).catch(function() {
// jasmine-ajax sets statusText to 'abort' when request.abort() is called
expect(request.statusText).toBe('abort');
done();
});
getAjaxRequest().then(function (req) {
// call cancel() when the request has been sent, but a response has not been received
source.cancel();
request = req;
});
});
});
});
+8 -1
View File
@@ -11,7 +11,14 @@ describe('instance', function () {
var instance = axios.create();
for (var prop in axios) {
if (['Axios', 'create', 'all', 'spread', 'default'].indexOf(prop) > -1) {
if ([
'Axios',
'create',
'Cancel',
'CancelToken',
'all',
'spread',
'default'].indexOf(prop) > -1) {
continue;
}
expect(typeof instance[prop]).toBe(typeof axios[prop]);
+16
View File
@@ -320,5 +320,21 @@ module.exports = {
});
});
});
},
testCancel: function(test) {
var source = axios.CancelToken.source();
server = http.createServer(function (req, res) {
// call cancel() when the request has been sent, but a response has not been received
source.cancel('Operation has been canceled.');
}).listen(4444, function() {
axios.get('http://localhost:4444/', {
cancelToken: source.token
}).catch(function (thrown) {
test.ok(thrown instanceof axios.Cancel, 'Promise must be rejected with a Cancel obejct');
test.equal(thrown.message, 'Operation has been canceled.');
test.done();
});
});
}
};