前言
网络请求是开发中最基础也是最核心的需求,封装一个稳定且可用性高的请求也显得尤为重要。通常封装的内容除了入参之外,更多的是请求中的异常处理。本文分享下我在处理 token
异常方面的做法,通过维护请求队列,实现重发请求,减少 token
重复请求。
公共请求方法
下面以封装微信小程序请求作为例子,这是一个基础的公共请求:
代码语言:javascript复制common({ baseUrl = this.baseUrl, method, url, data, header }) {
return new Promise((resolve, reject) => {
let token = wx.$utils.getStorageToken()
wx.request({
method,
url: baseUrl url,
data,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
token,
...header
},
success: (res) => {
if (res.data.code == 0 || res.data.code == 500) { // 失败
reject(res.data)
}
if (res.data.code == 1) { // 成功
resolve(res.data)
}
if (res.data.code == -1) { // token过期
// token过期处理
}
},
fail: reject
})
})
}
token过期重发请求
getToken
方法内部会将 token
存储到本地中
success: (res) => {
res = res.data
if (res.code == 0) {
reject(res.msg)
}
if (res.code == 1) {
wx.setStorageSync('loginInfo', res.data)
resolve(res.data.token)
}
}
当 token
过期,在等待 getToken
后,再次发送请求,将结果 resolve
common({ baseUrl = this.baseUrl, method, url, data, header }) {
return new Promise((resolve, reject) => {
let token = wx.$utils.getStorageToken()
wx.request({
method,
url: baseUrl url,
data,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
token,
...header
},
success: async (res) => {
if (res.data.code == 0 || res.data.code == 500) {
reject(res.data)
}
if (res.data.code == 1) {
resolve(res.data)
}
if (res.data.code == -1) {
await this.getToken()
this.common({ baseUrl, method, url, data, header })
.then(resolve)
.catch(reject)
}
},
fail: reject
})
})
}
这样看起来好像没什么问题,但由于内部没有限制处理,有 n 个请求就会发起 n 个 getToken
请求。这当然不是我们想要的,就像下面这样重复发起了两次 wxLogin
:
维护请求队列
理想的情况是:token
过期后,发起一个 getToken
请求。每当有请求进来,将它存入队列中,等待 getToken
完成,执行队列中的所有请求。
这样我们需要定义请求队列 qeueu
和token
请求的标识 isTokening
,还有加入队列方法 pushQeueu
和执行队列方法 execQeueu
。
{
qeueu: [],
isTokening: false,
pushQeueu({ method, url, data, header, resolve, reject }){
this.qeueu.push({
data: {
method, url, data, header
},
resolve,
reject,
request: (data)=> this.common(data)
})
},
execQeueu(){
this.qeueu.forEach((item, index) => {
item.request(item.data)
.then(item.resolve)
.catch(item.reject)
// 执行完任务后 清空队列
if(index === this.qeueu.length-1){
this.qeueu.length = 0
}
})
}
}
处理如下:
代码语言:javascript复制common({ baseUrl = this.baseUrl, method, url, data, header }) {
return new Promise((resolve, reject) => {
let token = wx.$utils.getStorageToken()
wx.request({
method,
url: baseUrl url,
data,
header: {
'Content-Type': 'application/x-www-form-urlencoded',
token,
...header
},
success: async (res) => {
if (res.data.code == 0 || res.data.code == 500) {
reject(res.data)
}
if (res.data.code == 1) {
resolve(res.data)
}
if (res.data.code == -1) {
this.pushQeueu({ method, url, data, header, resolve, reject })
if(this.isTokening === false){
this.isTokening = true
await this.getToken()
this.isTokening = false
this.execQeueu()
}
}
},
fail: reject
})
})
}
发起 getToken
请求后,将 isTokening
置为 true
表示正在请求中。当再有请求进入时,则不会再重复发送 getToken
。
处理getToken错误
getToken
在发生错误时,我们应当捕获错误,不继续执行请求队列并清空队列
if (res.data.code == -1) {
this.pushQeueu({ method, url, data, header, resolve })
if(this.isTokening === false){
this.isTokening = true
let err = await this.getToken().then(res => null).catch(err => err)
if(err){
this.qeueu.length = 0
console.error(err)
}else{
this.isTokening = false
this.execQeueu()
}
}
}
写在最后
以上是我在处理 token 异常的做法,如果你有更好的做法或建议,欢迎交流~