New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding support for request cancellation #452
Changes from all commits
b2bc335
df50698
72dd897
2033ef3
5efca1e
920769d
216e2a6
032916e
e9fbe95
8f30490
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
language: node_js | ||
node_js: | ||
- node | ||
email: | ||
on_failure: change | ||
on_success: never | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
'use strict'; | ||
|
||
/** | ||
* A `Cancel` is an object that is thrown when an operation is canceled. | ||
* | ||
* @class | ||
* @param {string=} message The message. | ||
*/ | ||
function Cancel(message) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this.message = message; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I said in a previous comment, I'd do |
||
} | ||
|
||
Cancel.prototype.toString = function toString() { | ||
return 'Cancel' + (this.message ? ': ' + this.message : ''); | ||
}; | ||
|
||
Cancel.prototype.__CANCEL__ = true; | ||
|
||
module.exports = Cancel; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
'use strict'; | ||
|
||
var Cancel = require('./Cancel'); | ||
|
||
/** | ||
* A `CancelToken` is an object that can be used to request cancellation of an operation. | ||
* | ||
* @class | ||
* @param {Function} executor The executor function. | ||
*/ | ||
function CancelToken(executor) { | ||
if (typeof executor !== 'function') { | ||
throw new TypeError('executor must be a function.'); | ||
} | ||
|
||
var resolvePromise; | ||
this.promise = new Promise(function promiseExecutor(resolve) { | ||
resolvePromise = resolve; | ||
}); | ||
|
||
var token = this; | ||
executor(function cancel(message) { | ||
if (token.reason) { | ||
// Cancellation has already been requested | ||
return; | ||
} | ||
|
||
token.reason = new Cancel(message); | ||
resolvePromise(token.reason); | ||
}); | ||
} | ||
|
||
/** | ||
* Throws a `Cancel` if cancellation has been requested. | ||
*/ | ||
CancelToken.prototype.throwIfRequested = function throwIfRequested() { | ||
if (this.reason) { | ||
throw this.reason; | ||
} | ||
}; | ||
|
||
/** | ||
* Returns an object that contains a new `CancelToken` and a function that, when called, | ||
* cancels the `CancelToken`. | ||
*/ | ||
CancelToken.source = function source() { | ||
var cancel; | ||
var token = new CancelToken(function executor(c) { | ||
cancel = c; | ||
}); | ||
return { | ||
token: token, | ||
cancel: cancel | ||
}; | ||
}; | ||
|
||
module.exports = CancelToken; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict'; | ||
|
||
module.exports = function isCancel(value) { | ||
return !!(value && value.__CANCEL__); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll end up using this version more frequently, because I find it clearer than the previous one (it's basically a deferred object). I'd put it before the
CancelToken
constructor version so people will find it more approachable.Maybe it's just a personal preference and not everybody will see it that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rubennorte Yeah, I agree. I've just updated README.md.