例如将 app-header.vue 的 created 钩⼦中调⽤多次 this.loadUserInfo(),设置 Token 失效并刷新⻚
⾯,此时观察请求。
// layout/components/app-header.vue
…
created () {
// 第⼀次请求
this.loadUserInfo()
// 第⼆次请求
this.loadUserInfo()
},
…
通过浏览器 Network ⾯板观察请求,发现有 2 次重复的刷新 Token 请求,由于 2 次刷新 Token 携带的
refresh_token 相同,会导致⼀次成功⼀次失败,⽽失败的⼀次会导致⻚⾯跳转登录⻚。
好⽐我去饮⽔机接⽔,我的朋友⼩明⽐我早到⼀步,但是发现饮⽔机的⽔桶空了,这时⼩明就将⼀桶新的⽔放进去。由于⼩明已经把⽔桶换过了,我就不⽤再去换⼀桶了,⽽是在他换完以后直接接⽔就可以了

为了避免多次请求重复刷新 Token,可以通过⼀个变量 isRefreshing 标记 Token 的刷新状态
默认状态为 false,并在发送刷新 Token 请求前检测,当状态为 false 才能发送。
发送刷新请求时,设置标记为 true
请求完毕,设置标记为 false
// layout/components/app-header.vue
…
// 是否正在更新 Token
let isRefreshing = false
request.interceptors.response.use(function (response) {
…
} else if (status === 401) {
if (!store.state.user) {…}
// 发送刷新请求前判断 isRefreshing 是否存在其他已发送的刷新请求
// 1 如果有,则将当前请求挂起,等到 Token 刷新完毕再重发,这⾥先设置为
return
if (isRefreshing) {
return
}
// 2. 如果没有,则更新 isRefreshing 并发送请求,继续执⾏后续操作
isRefreshing = true
// 发送刷新请求
return request({
…
}).then(res => {
…
}).catch(() => {
…
}).finally(() => {
// 3 请求完毕,⽆论成功失败,设置 isRefreshing 为 false
isRefreshing = false
})
} else if (status === 403) {
…
处理完毕,观察浏览器 Network ⾯板,refresh_token 请求减少为 1 次。

虽然刷新 Token 的问题解决了,但之前发送的两个请求只有⼀个成功执⾏,其余的请求被阻⽌了。
(旧⽅式)我们需要将未成功触发 refresh_token 的请求从中⽌更改为挂起,可以通过 Promise 对象来实现。
挂起请求时创建⼀个 Promise 对象,将该 promise 的 resolve 统⼀保存。
当 Token 刷新完成,将统⼀保存的所有 resolve 调⽤。
我们声明⼀个数组存储所有被挂起的请求,当 Token 刷新完毕再将这些请求重新发送。
// layout/components/app-header.vue
…
// 存储因等待 Token 刷新⽽挂起的请求
let requests = []
// 设置响应拦截器
request.interceptors.response.use(function (resp
…
}, function (error) {
…
if (isRefreshing) {
// 将发送请求保存在函数中,存储到 requests 中等待执⾏,并 return
中⽌操作
return requests.push(() => {
request(error.config)
})
}
isRefreshing = true
return request({
…
}).then(res => {
…
store.commit(‘setUser’, res.data.content
// Token 刷新成功后,将 requests 中的请求重新发送
requests.forEach(callback => callback())
// 随后清空已被重新发送的请求
requests = []
return request(error.config)
}).catch(() => {
…
浏览器测试后发现,刷新 Token 请求⼀次,其他请求也可正常发送。
