小蚊子前端
高级前端工程师ios
咱们在先后端交互的过程当中,一般是经过请求接口来实现的,而一个页面中的交互又很是复杂,例如须要屡次频繁请求同一个接口,或者在接口还没返回时就要切换路由等。这些都须要对接口请求的时机或者请求接口以后进行处理,避免一些无用的请求或者接口返回顺序的差别。git
咱们在以前的如何实现 axios 的自定义适配器 adapter文章里,略过了 axios 是如何主动取消当前请求的。今天咱们就将一下在 axios 中如何取消以前发起的请求,源码中又是怎样实现的。github
咱们先来看下 axios 中取消请求的用法:axios
const CancelToken = axios.CancelToken; // 返回两个字段,{ token, cancel } // token用于表示某个请求,是一个Promise类型 // cancel是一个方法,当被调用时,则取消token注入的那个请求 const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, // 将token注入到请求中 }) .catch(function (thrown) { // 判断是不是因主动取消致使的 if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error console.error(thrown); } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // 主动取消请求 // cancel方法会把注入的同一个token的请求方法一并取消掉 // 上面的get和post请求都会被取消掉 // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.');
从 demo 上来看,用法很简单,token 和 cancel 的关系对应上便可。后端
官方例子中还有一种取消请求的方式,这个咱们放在后面讲,更容易理解一些。promise
取消请求的方法在 https://github.com/axios/axios/tree/master/lib/cancel 的目录中,3 个文件:markdown
source 做为取消请求的入口,咱们就先来看下 source 方法。前端工程师
// 建立token和cancel方法 CancelToken.source = function source() { var cancel; // token为 CancelToken 的实例,包含 promise 和 reason 两个属性 // 同时把 executor 中的参数给到 cancel // 即CancelToken有一个回调函数,而这个回调函数的参数也是一个函数 // CancelToken怎么执行,咱们接着看! var token = new CancelToken(function executor(c) { cancel = c; }); return { token: token, cancel: cancel, }; };
CancelToken 用来取消请求,但我理解起来,思路很是的绕,咱们一点点来剖析:ide
function CancelToken(executor) { if (typeof executor !== 'function') { throw new TypeError('executor must be a function.'); } // 建立一个Promise的实例, // 当resolvePromise执行时,this.promise变为fulfilled状态 var resolvePromise; this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; }); // new一个实例时,会当即执行CancelToken的回调函数executor方法 // executor的参数也是一个函数,即上面的cancel就是当前的cancel函数体 // 当executor的回调函数cancel执行时,会给当前CancelToken建立一个reason属性,这个属性是Cancel的实例 // 并执行resolvePromise方法,将reason实例穿进去;执行后this.promise变为fulfilled状态 var token = this; executor(function cancel(message) { if (token.reason) { // Cancellation has already been requested return; } token.reason = new Cancel(message); resolvePromise(token.reason); }); }
也就是说会先建立一个 CancelToken 的实例 token,同时,将 CancelToken 中回调函数的参数给到了 cancel。当 cancel 执行时,则 token 中的 promise 属性则会从 pending 状态变为 fulfilled 状态,那么 promise 上挂载的then()方法也就能够继续执行了。
在调用 cancel 方法后,请求中是怎么操做的呢?咱们看下adapter/xhr.js中的代码:
if (config.cancelToken) { // config.cancelToken就是上面建立的token // 当token.promise变为fulfilled状态后,就能够执行后续的链式操做 // Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } // 取消当前的请求 request.abort(); // 将Cancel的实例cancel给到reject reject(cancel); // Clean up request request = null; }); }
当咱们使用 axios 的 catch 捕获内部抛出的异常时,就能够经过isCancel判断是不是因主动取消请求致使的异常:
axios .get('/user/12345', { cancelToken: source.token, // 将token注入到请求中 }) .catch(function (thrown) { // 判断是不是因主动取消致使的 if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error console.error(thrown); } });
如今再来看下 cancel 执行的整个流程,就会清晰流畅不少。
咱们在第 1 节还留着一个问题,axios 取消请求还有另外一种方式,即直接使用 CancelToken 类。
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { // CancelToken建立的 cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }), }); // cancel the request cancel();
其实咱们发现,source()方法,只是给咱们额外又封装了一下,简单的返回了 token 和 cancel,但本质仍是 CancelToken 中的东西。
在取消请求的过程当中,token 要和 cancel 方法保持对应关系,即都在一个对象里;若其余的请求也要取消时,能够额外再生成一组 token 和 cancel。同时,这里还用到了 Promise 的一个机制,只有在当前 Promise 变动为 fulfilled 状态后,才能执行后面的 then 等操做。