From b2bc3354ac22e76e129ef8ae5b9656fa555fa061 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Thu, 15 Sep 2016 21:06:32 -0700 Subject: [PATCH 1/8] Adding Cancel and CancelToken classes --- lib/cancel/Cancel.js | 17 ++++++ lib/cancel/CancelToken.js | 57 ++++++++++++++++++ test/specs/cancel/Cancel.spec.js | 15 +++++ test/specs/cancel/CancelToken.spec.js | 87 +++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 lib/cancel/Cancel.js create mode 100644 lib/cancel/CancelToken.js create mode 100644 test/specs/cancel/Cancel.spec.js create mode 100644 test/specs/cancel/CancelToken.spec.js diff --git a/lib/cancel/Cancel.js b/lib/cancel/Cancel.js new file mode 100644 index 0000000..99d01ff --- /dev/null +++ b/lib/cancel/Cancel.js @@ -0,0 +1,17 @@ +'use strict'; + +/** + * A `Cancel` is an object that is thrown when an operation is canceled. + * + * @class + * @param {string=} message The message. + */ +function Cancel(message) { + this.message = message; +} + +Cancel.prototype.toString = function toString() { + return 'Cancel' + (this.message ? ': ' + this.message : ''); +}; + +module.exports = Cancel; diff --git a/lib/cancel/CancelToken.js b/lib/cancel/CancelToken.js new file mode 100644 index 0000000..6b46e66 --- /dev/null +++ b/lib/cancel/CancelToken.js @@ -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; diff --git a/test/specs/cancel/Cancel.spec.js b/test/specs/cancel/Cancel.spec.js new file mode 100644 index 0000000..0e0de80 --- /dev/null +++ b/test/specs/cancel/Cancel.spec.js @@ -0,0 +1,15 @@ +var Cancel = require('../../../lib/cancel/Cancel'); + +describe('Cancel', function() { + describe('toString', function() { + it('returns correct result when message is not specified', function() { + var cancel = new Cancel(); + expect(cancel.toString()).toBe('Cancel'); + }); + + it('returns correct result when message is specified', function() { + var cancel = new Cancel('Operation has been canceled.'); + expect(cancel.toString()).toBe('Cancel: Operation has been canceled.'); + }); + }); +}); diff --git a/test/specs/cancel/CancelToken.spec.js b/test/specs/cancel/CancelToken.spec.js new file mode 100644 index 0000000..dd72327 --- /dev/null +++ b/test/specs/cancel/CancelToken.spec.js @@ -0,0 +1,87 @@ +var CancelToken = require('../../../lib/cancel/CancelToken'); +var Cancel = require('../../../lib/cancel/Cancel'); + +describe('CancelToken', function() { + describe('constructor', function() { + it('throws when executor is not specified', function() { + expect(function() { + new CancelToken(); + }).toThrowError(TypeError, 'executor must be a function.'); + }); + + it('throws when executor is not a function', function() { + expect(function() { + new CancelToken(123); + }).toThrowError(TypeError, 'executor must be a function.'); + }); + }); + + describe('reason', function() { + it('returns a Cancel if cancellation has been requested', function() { + var cancel; + var token = new CancelToken(function(c) { + cancel = c; + }); + cancel('Operation has been canceled.'); + expect(token.reason).toEqual(jasmine.any(Cancel)); + expect(token.reason.message).toBe('Operation has been canceled.'); + }); + + it('returns undefined if cancellation has not been requested', function() { + var token = new CancelToken(function() {}); + expect(token.reason).toBeUndefined(); + }); + }); + + describe('promise', function() { + it('returns a Promise that resolves when cancellation is requested', function(done) { + var cancel; + var token = new CancelToken(function(c) { + cancel = c; + }); + token.promise.then(function onFulfilled(value) { + expect(value).toEqual(jasmine.any(Cancel)); + expect(value.message).toBe('Operation has been canceled.'); + done(); + }); + cancel('Operation has been canceled.'); + }); + }); + + describe('throwIfRequested', function() { + it('throws if cancellation has been requested', function() { + // Note: we cannot use expect.toThrowError here as Cancel does not inherit from Error + var cancel; + var token = new CancelToken(function(c) { + cancel = c; + }); + cancel('Operation has been canceled.'); + try { + token.throwIfRequested(); + fail('Expected throwIfRequested to throw.'); + } catch (thrown) { + if (!(thrown instanceof Cancel)) { + fail('Expected throwIfRequested to throw a Cancel, but it threw ' + thrown + '.'); + } + expect(thrown.message).toBe('Operation has been canceled.'); + } + }); + + it('does not throw if cancellation has not been requested', function() { + var token = new CancelToken(function() {}); + token.throwIfRequested(); + }); + }); + + describe('source', function() { + it('returns an object containing token and cancel function', function() { + var source = CancelToken.source(); + expect(source.token).toEqual(jasmine.any(CancelToken)); + expect(source.cancel).toEqual(jasmine.any(Function)); + expect(source.token.reason).toBeUndefined(); + source.cancel('Operation has been canceled.'); + expect(source.token.reason).toEqual(jasmine.any(Cancel)); + expect(source.token.reason.message).toBe('Operation has been canceled.'); + }); + }); +}); From 72dd897bb584e2730980f5d211cf783186a2482b Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Sat, 17 Sep 2016 11:52:56 -0700 Subject: [PATCH 2/8] Adding cancellation support --- lib/adapters/http.js | 9 +++++ lib/adapters/xhr.js | 9 +++++ lib/axios.js | 4 +++ lib/core/dispatchRequest.js | 36 +++++++++++++++----- test/specs/cancel.spec.js | 66 +++++++++++++++++++++++++++++++++++++ test/specs/instance.spec.js | 9 ++++- test/unit/adapters/http.js | 16 +++++++++ 7 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 test/specs/cancel.spec.js diff --git a/lib/adapters/http.js b/lib/adapters/http.js index 4d76a01..95e4e8c 100644 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -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); diff --git a/lib/adapters/xhr.js b/lib/adapters/xhr.js index b0035d7..1fb890b 100644 --- a/lib/adapters/xhr.js +++ b/lib/adapters/xhr.js @@ -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; diff --git a/lib/axios.js b/lib/axios.js index 8937b80..3fc1f32 100644 --- a/lib/axios.js +++ b/lib/axios.js @@ -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); diff --git a/lib/core/dispatchRequest.js b/lib/core/dispatchRequest.js index fc52c03..5aa86d9 100644 --- a/lib/core/dispatchRequest.js +++ b/lib/core/dispatchRequest.js @@ -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); }); }; diff --git a/test/specs/cancel.spec.js b/test/specs/cancel.spec.js new file mode 100644 index 0000000..d02d8e3 --- /dev/null +++ b/test/specs/cancel.spec.js @@ -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; + }); + }); + }); +}); diff --git a/test/specs/instance.spec.js b/test/specs/instance.spec.js index 4fac13d..be34230 100644 --- a/test/specs/instance.spec.js +++ b/test/specs/instance.spec.js @@ -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]); diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js index ba59558..1e41a63 100644 --- a/test/unit/adapters/http.js +++ b/test/unit/adapters/http.js @@ -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(); + }); + }); } }; From 2033ef3ad01ae3c83dc0e39adddf682852b17939 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Sat, 17 Sep 2016 12:49:14 -0700 Subject: [PATCH 3/8] Adding TypeScript definitions for cancel tokens --- axios.d.ts | 31 +++++++++++++++++++++++++++++++ test/typescript/axios.ts | 31 +++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/axios.d.ts b/axios.d.ts index d672175..88d283a 100644 --- a/axios.d.ts +++ b/axios.d.ts @@ -41,6 +41,7 @@ export interface AxiosRequestConfig { httpAgent?: any; httpsAgent?: any; proxy?: AxiosProxyConfig; + cancelToken?: CancelToken; } export interface AxiosResponse { @@ -66,6 +67,34 @@ export interface Promise { export interface AxiosPromise extends Promise { } +export interface CancelStatic { + new (message?: string): Cancel; +} + +export interface Cancel { + message: string; +} + +export interface Canceler { + (message?: string): void; +} + +export interface CancelTokenStatic { + new (executor: (cancel: Canceler) => void): CancelToken; + source(): CancelTokenSource; +} + +export interface CancelToken { + promise: Promise; + reason?: Cancel; + throwIfRequested(): void; +} + +export interface CancelTokenSource { + token: CancelToken; + cancel: Canceler; +} + export interface AxiosInterceptorManager { use(onFulfilled: (value: V) => V | Promise, onRejected?: (error: any) => any): number; eject(id: number): void; @@ -90,6 +119,8 @@ export interface AxiosStatic extends AxiosInstance { (config: AxiosRequestConfig): AxiosPromise; (url: string, config?: AxiosRequestConfig): AxiosPromise; create(config?: AxiosRequestConfig): AxiosInstance; + Cancel: CancelStatic; + CancelToken: CancelTokenStatic; all(values: (T | Promise)[]): Promise; spread(callback: (...args: T[]) => R): (array: T[]) => R; } diff --git a/test/typescript/axios.ts b/test/typescript/axios.ts index 9f25f1b..39571a9 100644 --- a/test/typescript/axios.ts +++ b/test/typescript/axios.ts @@ -1,4 +1,15 @@ -import axios, { AxiosRequestConfig, AxiosResponse, AxiosError, AxiosInstance, AxiosAdapter } from '../../'; +import axios, { + AxiosRequestConfig, + AxiosResponse, + AxiosError, + AxiosInstance, + AxiosAdapter, + Cancel, + CancelToken, + CancelTokenSource, + Canceler +} from '../../'; + import { Promise } from 'es6-promise'; const config: AxiosRequestConfig = { @@ -30,7 +41,8 @@ const config: AxiosRequestConfig = { proxy: { host: '127.0.0.1', port: 9000 - } + }, + cancelToken: new axios.CancelToken((cancel: Canceler) => {}) }; const handleResponse = (response: AxiosResponse) => { @@ -210,3 +222,18 @@ axios.get('/user') axios.get('/user') .catch((error: any) => Promise.resolve('foo')) .then((value: string) => {}); + +// Cancellation + +const source: CancelTokenSource = axios.CancelToken.source(); + +axios.get('/user', { + cancelToken: source.token +}).catch((thrown: AxiosError | Cancel) => { + if (thrown instanceof axios.Cancel) { + const cancel: Cancel = thrown; + console.log(cancel.message); + } +}); + +source.cancel('Operation has been canceled.'); From 5efca1ebbc9df85ff88758a9523480eccca531d7 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Sun, 18 Sep 2016 14:16:27 -0700 Subject: [PATCH 4/8] Updating docs --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c42a02..bb6abdc 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,12 @@ These are the available config options for making requests. Only the `url` is re proxy: { host: '127.0.0.1', port: 9000 - } + }, + + // `cancelToken` specifies a cancel token that can be used to cancel the request + // (see Cancellation section below for details) + cancelToken: new CancelToken(function (cancel) { + }) } ``` @@ -457,6 +462,51 @@ axios.get('/user/12345', { }) ``` +## Cancellation + +You can cancel a request using a *cancel token*. + +> The axios cancel token API is based on [cancelable promises proposal](https://github.com/tc39/proposal-cancelable-promises), which is currently at Stage 1. + +You can create a cancel token by passing an executor function to the `CancelToken` constructor as shown below: + +```js +var Cancel = axios.Cancel; +var CancelToken = axios.CancelToken; + +var cancel; + +axios.get('/user/12345', { + cancelToken: new CancelToken(function executor(c) { + // An executor function receives a cancel function as a parameter + // You can use the cancel function to cancel the request later + cancel = c; + }) +}).catch(function(thrown) { + if (thrown instanceof Cancel) { + console.log('Request canceled', thrown.message); + } else { + // handle error + } +}); + +// cancel the request (the message parameter is optional) +cancel('Operation canceled by the user.'); +``` + +You can also create a cancel token using the `CancelToken.source` factory: + +```js +var CancelToken = axios.CancelToken; +var source = CancelToken.source(); + +axios.get('/user/12345', { + cancelToken: source.token +}); + +source.cancel(); +``` + ## Semver Until axios reaches a `1.0` release, breaking changes will be released with a new minor version. For example `0.5.1`, and `0.5.4` will have the same API, but `0.6.0` will have breaking changes. From 920769d0d74b6f37c64e715051aa7ed1ee50fc74 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Tue, 20 Sep 2016 19:54:24 -0700 Subject: [PATCH 5/8] Improve docs --- README.md | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index bb6abdc..862e48a 100644 --- a/README.md +++ b/README.md @@ -466,22 +466,17 @@ axios.get('/user/12345', { You can cancel a request using a *cancel token*. -> The axios cancel token API is based on [cancelable promises proposal](https://github.com/tc39/proposal-cancelable-promises), which is currently at Stage 1. +> The axios cancel token API is based on the [cancelable promises proposal](https://github.com/tc39/proposal-cancelable-promises), which is currently at Stage 1. -You can create a cancel token by passing an executor function to the `CancelToken` constructor as shown below: +You can create a cancel token using the `CancelToken.source` factory as shown below: ```js var Cancel = axios.Cancel; var CancelToken = axios.CancelToken; - -var cancel; +var source = CancelToken.source(); axios.get('/user/12345', { - cancelToken: new CancelToken(function executor(c) { - // An executor function receives a cancel function as a parameter - // You can use the cancel function to cancel the request later - cancel = c; - }) + cancelToken: source.token }).catch(function(thrown) { if (thrown instanceof Cancel) { console.log('Request canceled', thrown.message); @@ -491,20 +486,24 @@ axios.get('/user/12345', { }); // cancel the request (the message parameter is optional) -cancel('Operation canceled by the user.'); +source.cancel('Operation canceled by the user.'); ``` -You can also create a cancel token using the `CancelToken.source` factory: +You can also create a cancel token by passing an executor function to the `CancelToken` constructor: ```js var CancelToken = axios.CancelToken; -var source = CancelToken.source(); +var cancel; axios.get('/user/12345', { - cancelToken: source.token + cancelToken: new CancelToken(function executor(c) { + // An executor function receives a cancel function as a parameter + cancel = c; + }) }); -source.cancel(); +// cancel the request +cancel(); ``` ## Semver From 216e2a6787fa836d0aeb0b000c2849ba88e2f462 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Wed, 21 Sep 2016 18:47:37 -0700 Subject: [PATCH 6/8] Adding axios.isCancel method --- README.md | 3 +-- axios.d.ts | 3 ++- lib/axios.js | 1 + lib/cancel/Cancel.js | 2 ++ lib/cancel/isCancel.js | 5 +++++ test/specs/api.spec.js | 6 ++++++ test/specs/cancel/isCancel.spec.js | 12 ++++++++++++ test/specs/instance.spec.js | 1 + test/typescript/axios.ts | 2 +- 9 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 lib/cancel/isCancel.js create mode 100644 test/specs/cancel/isCancel.spec.js diff --git a/README.md b/README.md index 862e48a..c325338 100644 --- a/README.md +++ b/README.md @@ -471,14 +471,13 @@ You can cancel a request using a *cancel token*. You can create a cancel token using the `CancelToken.source` factory as shown below: ```js -var Cancel = axios.Cancel; var CancelToken = axios.CancelToken; var source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function(thrown) { - if (thrown instanceof Cancel) { + if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error diff --git a/axios.d.ts b/axios.d.ts index 88d283a..03c7f1b 100644 --- a/axios.d.ts +++ b/axios.d.ts @@ -120,7 +120,8 @@ export interface AxiosStatic extends AxiosInstance { (url: string, config?: AxiosRequestConfig): AxiosPromise; create(config?: AxiosRequestConfig): AxiosInstance; Cancel: CancelStatic; - CancelToken: CancelTokenStatic; + CancelToken: CancelTokenStatic; + isCancel(value: any): boolean; all(values: (T | Promise)[]): Promise; spread(callback: (...args: T[]) => R): (array: T[]) => R; } diff --git a/lib/axios.js b/lib/axios.js index 3fc1f32..aa741f0 100644 --- a/lib/axios.js +++ b/lib/axios.js @@ -37,6 +37,7 @@ axios.create = function create(defaultConfig) { // Expose Cancel & CancelToken axios.Cancel = require('./cancel/Cancel'); axios.CancelToken = require('./cancel/CancelToken'); +axios.isCancel = require('./cancel/isCancel'); // Expose all/spread axios.all = function all(promises) { diff --git a/lib/cancel/Cancel.js b/lib/cancel/Cancel.js index 99d01ff..e0de400 100644 --- a/lib/cancel/Cancel.js +++ b/lib/cancel/Cancel.js @@ -14,4 +14,6 @@ Cancel.prototype.toString = function toString() { return 'Cancel' + (this.message ? ': ' + this.message : ''); }; +Cancel.prototype.__CANCEL__ = true; + module.exports = Cancel; diff --git a/lib/cancel/isCancel.js b/lib/cancel/isCancel.js new file mode 100644 index 0000000..051f3ae --- /dev/null +++ b/lib/cancel/isCancel.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function isCancel(value) { + return !!(value && value.__CANCEL__); +}; diff --git a/test/specs/api.spec.js b/test/specs/api.spec.js index 937f124..e177d07 100644 --- a/test/specs/api.spec.js +++ b/test/specs/api.spec.js @@ -34,6 +34,12 @@ describe('static api', function () { it('should have factory method', function () { expect(typeof axios.create).toEqual('function'); }); + + it('should have Cancel, CancelToken, and isCancel properties', function () { + expect(typeof axios.Cancel).toEqual('function'); + expect(typeof axios.CancelToken).toEqual('function'); + expect(typeof axios.isCancel).toEqual('function'); + }); }); describe('instance api', function () { diff --git a/test/specs/cancel/isCancel.spec.js b/test/specs/cancel/isCancel.spec.js new file mode 100644 index 0000000..e6be40d --- /dev/null +++ b/test/specs/cancel/isCancel.spec.js @@ -0,0 +1,12 @@ +var isCancel = require('../../../lib/cancel/isCancel'); +var Cancel = require('../../../lib/cancel/Cancel'); + +describe('isCancel', function() { + it('returns true if value is a Cancel', function() { + expect(isCancel(new Cancel())).toBe(true); + }); + + it('returns false if value is not a Cancel', function() { + expect(isCancel({ foo: 'bar' })).toBe(false); + }); +}); diff --git a/test/specs/instance.spec.js b/test/specs/instance.spec.js index be34230..fe395f3 100644 --- a/test/specs/instance.spec.js +++ b/test/specs/instance.spec.js @@ -16,6 +16,7 @@ describe('instance', function () { 'create', 'Cancel', 'CancelToken', + 'isCancel', 'all', 'spread', 'default'].indexOf(prop) > -1) { diff --git a/test/typescript/axios.ts b/test/typescript/axios.ts index 39571a9..d3d3198 100644 --- a/test/typescript/axios.ts +++ b/test/typescript/axios.ts @@ -230,7 +230,7 @@ const source: CancelTokenSource = axios.CancelToken.source(); axios.get('/user', { cancelToken: source.token }).catch((thrown: AxiosError | Cancel) => { - if (thrown instanceof axios.Cancel) { + if (axios.isCancel(thrown)) { const cancel: Cancel = thrown; console.log(cancel.message); } From e9fbe959d2b4a44766eec2e871bf00f8cfa0c245 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Fri, 23 Sep 2016 15:58:25 -0700 Subject: [PATCH 7/8] Updating dispatchRequest to use isCancel instead of instanceof --- lib/core/dispatchRequest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/core/dispatchRequest.js b/lib/core/dispatchRequest.js index 9146f27..b511101 100644 --- a/lib/core/dispatchRequest.js +++ b/lib/core/dispatchRequest.js @@ -2,7 +2,7 @@ var utils = require('./../utils'); var transformData = require('./transformData'); -var Cancel = require('../cancel/Cancel'); +var isCancel = require('../cancel/isCancel'); var defaults = require('../defaults'); /** @@ -61,7 +61,7 @@ module.exports = function dispatchRequest(config) { return response; }, function onAdapterRejection(reason) { - if (!(reason instanceof Cancel)) { + if (!isCancel(reason)) { throwIfCancellationRequested(config); // Transform response data From 8f304903863800f1efe9d4fd15f7611d1b0d4e15 Mon Sep 17 00:00:00 2001 From: Nick Uraltsev Date: Fri, 23 Sep 2016 16:26:42 -0700 Subject: [PATCH 8/8] Attempting to fix Travis build --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 71c72cc..9d4506d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ language: node_js +node_js: + - node email: on_failure: change on_success: never