方式一ios
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
source.cancel('Operation canceled by the user.');
複製代碼
方式二axios
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
cancel();
複製代碼
在axios/lib/axios.js
中定義了跟取消相關的方法promise
var axios = createInstance(defaults);
...
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
...
複製代碼
我先看CancelToken,進入到axios/lib/cancel/CancelToken.js
文件bash
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(cancel方法);
executor(function cancel(message) {
if (token.reason) {
// Cancellation has already been requested
return;
}
//token.reason是Cancel的實例
token.reason = new Cancel(message);
resolvePromise(token.reason);//改變promise的狀態
});
}
CancelToken.source = function source() {
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
return {
token: token, //CancelToken的實例
cancel: cancel //function cancel(message) {...}
};
};
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
throw this.reason;
}
};
複製代碼
CancelToken構造函數,定義了一個promise,狀態一直是padding,將決定狀態的resolve方法傳遞cancel方法內部,cancel方法做爲參數傳到外部executor方法中。函數
CancelToken.source實際上new 一個CancelToken的實例,上述的cancel方法傳給cancel變量源碼分析
進入到axios/lib/cancel/Cancel.js
文件ui
function Cancel(message) {
this.message = message;
}
Cancel.prototype.toString = function toString() {
return 'Cancel' + (this.message ? ': ' + this.message : '');
};
Cancel.prototype.__CANCEL__ = true;
複製代碼
Cancel構造函數,有屬性message,Cancel.prototype有屬性__CANCEL__this
進入到axios/lib/cancel/isCancel.js
文件spa
function isCancel(value) {
return !!(value && value.__CANCEL__);
};
複製代碼
如何一步步實現取消的?prototype
function throwIfCancellationRequested(config) {
//判斷是否配置有cancelToken,
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
function dispatchRequest(config) {
throwIfCancellationRequested(config);//請求前
...
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);//請求後,resolve
...
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) { //請求後,reject
throwIfCancellationRequested(config);
...
});
}
複製代碼
調用throwIfCancellationRequested方法,內部實現先判斷是否配置有cancelToken,則調用CancelToken.prototype.throwIfRequested,若是調用cancel操做過了確定reason存在,拋出throw reason,reason其實是Cancel的實例
在axios/lib/adapters/xhr.js
中
function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
...
if (config.cancelToken) {
//請求中,監聽cancelToken中promise狀態改變
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
request.abort();
reject(cancel);
request = null;
});
}
...
}
}
複製代碼
總結: CancelToken.source().token實際可用理解爲new 一個promise,狀態一直是 padding。把可用改變狀態方法傳到cancel方法中。外部調用cancel實現取消請求。
cancel操做,實際上是執行resolvePromise,讓cancelToken中promise狀態變爲已完成。實際上在發送請求前、請求中,請求後resolve、請求後reject四部分,都會去判斷是否有cancel操做。