使用axios在request拦截器中取消请求

最近在工作中遇到这样的需求,新用户在第一次登录的时候需要强制修改密码。刚开始接到这个需求的时候,心里想着在axios的请求拦截器中统一处理就可以了,判断用户是否需要修改密码,如果需要修改密码就返回一个reject,从而不发送请求。好像很简单啊,说干就干。
首先照着上面的想法,撸出下列代码:

http.interceptors.request.use(config => { if (needChangePassword) { return Promise.reject(new Error('changePassword')) } else { config.cancelToken = store.source.token return config } }, err => { return Promise.reject(err) })

心里想着,如果需要修改密码,就调用reject,然后在每个发送请求的外面都会有catch异常,在catch中统一处理错误信息,so beautiful,保存->运行,唉唉唉,怎么reject之后跑到外面的then里面去了,不是应该跳catch吗?有毒啊。
各种debugger之后,发现无论怎么reject,就是不跳catch。在走投无路之下,只能求助源码了,最后在源码core->Axios.js中发现了promise的调用顺序,主要源码如下:
var chain = [dispatchRequest, undefined]; var promise = Promise.resolve(config); this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); }

看完之后,才不得不感叹下,大牛果然就是不一样。源码的大致意思是这样的,首先处理request拦截器相关的promise,然后再发送请求,然后再处理respose拦截器相关promise,通过一个while循环生成了一个promsie链,最后返回的promise相当于下面:
promise = promise.then(request.fuifilled1, request.reject1) .then(request.fuifilled2, request.reject)... .then(dispatchRequest, undefined) .then(response.fuifilled1, response.reject1) .then(response.fuifilled2, response.reject2)... .then(response.fuifilledn, response.rejectn)

这个chain真是用的好,又学习到了。最后回到上面的问题,应该就很简单了,在request的fuifilled中reject,然后到undefined,然后到response中的reject。由于最开始没有在response的reject处理changePassword的错误,并且没有在reject中始终返回reject,所有就跑到外面的then里面,而没有跳到catch中。
最后附上修改后的代码:
http.interceptors.request.use(config => { if (needChangePassword) { return Promise.reject(new Error('changePassword')) } else { config.cancelToken = store.source.token return config } }, err => { return Promise.reject(err) }) // 配置拦截器与错误处理 http.interceptors.response.use(function (response) { const code = _.get(response, 'data.code') if (code !== 10000) { if (code === 10008) { window.location.href = 'https://www.it610.com/' } else { isDev && console.error(response, '请求返回状态码不为10000') throw new Error(response.data.message) } } else { return response.data.data ? response.data.data : response.data } }, function (error) { if (axios.isCancel(error)) { throw new Error('cancelled') } else if (error.message === 'changePassword') { throw new Error('changePassword') } else { if (error.response) { if (isDev) { console.group('请求发生错误') console.log(error.response.data) console.log(error.response.status) console.log(error.response.headers) console.groupEnd() } throw new Error('接口异常, 请联系管理员') } } })

【使用axios在request拦截器中取消请求】最后感叹下,源码还是个好东西,既能加深理解,又能学习知识。

    推荐阅读