最近在工作中遇到這樣的需求夺蛇,新用戶在第一次登錄的時候需要強制修改密碼步做。剛開始接到這個需求的時候副渴,心里想著在axios的請求攔截器中統(tǒng)一處理就可以了,判斷用戶是否需要修改密碼全度,如果需要修改密碼就返回一個reject煮剧,從而不發(fā)送請求。好像很簡單啊将鸵,說干就干勉盅。
首先照著上面的想法,擼出下列代碼:
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)
})
心里想著顶掉,如果需要修改密碼草娜,就調(diào)用reject,然后在每個發(fā)送請求的外面都會有catch異常痒筒,在catch中統(tǒng)一處理錯誤信息宰闰,so beautiful茬贵,保存->運行,唉唉唉移袍,怎么reject之后跑到外面的then里面去了解藻,不是應該跳catch嗎?有毒啊咐容。
各種debugger之后舆逃,發(fā)現(xiàn)無論怎么reject,就是不跳catch戳粒。在走投無路之下路狮,只能求助源碼了,最后在源碼core->Axios.js中發(fā)現(xiàn)了promise的調(diào)用順序蔚约,主要源碼如下:
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,然后再發(fā)送請求树枫,然后再處理respose攔截器相關promise直焙,通過一個while循環(huán)生成了一個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 = '/'
} else {
isDev && console.error(response, '請求返回狀態(tài)碼不為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('請求發(fā)生錯誤')
console.log(error.response.data)
console.log(error.response.status)
console.log(error.response.headers)
console.groupEnd()
}
throw new Error('接口異常, 請聯(lián)系管理員')
}
}
})
最后感嘆下,源碼還是個好東西文兑,既能加深理解盒刚,又能學習知識。