谢谢大家,想请大家帮忙看看这个封装的请求怎样去修改?
export const request = (options = {}) => { //异步封装接口,使用Promise处理异步请求
return new Promise((resolve, reject) => {
let rd3_key = uni.getStorageSync('rd3_key') ? uni.getStorageSync('rd3_key') : ''
// 发送请求
uni.request({
url: baseUrl + options.url || '', //接收请求的API
method: options.method || 'GET', //接收请求的方式,如果不传默认为GET
data: options.data || {}, //接收请求的data,不传默认为空
data: {
...options.data,
rd3_key
},
header: {
'content-type': 'application/x-www-form-urlencoded', //修改此处即可
}
}).then(data => {
let [err, res] = data;
// if(res.data.code == 4001){
// post_login()
// }
if(res.data.code == 4001){
// return post_login().then(resolve, reject)
post_login()
}
resolve(res);
}).catch(error => {
reject(error);
})
})
}
下面这段
if(res.data.code == 4001){ // return post_login().then(resolve, reject)
post_login()
}
是登录失效后 接口请求返回4001,这时候会调用post_login() 重新获取最新的登录态完成静默登录,这个过程没什么问题,但是我写的onload事件里的那些业务接口却不会重新再发起请求了,请问这种情况 怎样统一处理下?
回答:
在解决你本来要问的问题之前,先说一个代码现在的问题。
看代码处理方式,uni.request()
返回的显然是一个 Promise 或者 Promise Like。无所谓,都不需要再封一层 Promise。按原来的逻辑,处理成
// 去掉了业务逻辑部分return ui.request().then(([res, data]) => data);
如果使用 async/await 的话,看起来逻辑会更清晰
// 仅示意,不含参数和业务逻辑request = async () => {
const [err, res] = await uni.reqeust();
return res;
}
接下来处理问题本身。问题本身要处理的是两个分支:
- 如果是
post_login
调用request
,不处理 4001 之类的错误(这个在问题中没明确描述) - 如果是其他调用
request
处理 4001 并尝试自动登录
先说第 2 个,如果要自动登录,那么这个 request
的逻辑大概是这样:
async function request() { let [err, res] = await businessRequest();
if (res.data.code === 4001) {
post_login();
([err, res] = await businessRequest()); // 再试一次
}
return res;
}
这段代码为了在 post_login()
后重试,有可能处理两次业务逻辑,所以需要封装到一个函数中以便复用,也就是上面示例代码中需要封装到 businessRequest()
中的内容,显然 request
中去掉尝试登录的逻辑,就是要封装的内容。所以这里有两种处理方式
- 从
request
中移除尝试登录的代码,把这个事情交给一个新的外层封装requestWithLogin
去干 - 直接在
request
中封装逻辑部分,像上面的示例那样(可以封装成一个局部函数,也可以是一个同级的外部函数)
这里按 2 的方式来处理,单独写一个同级函数。要按 1 或局部函数的处理,只需要少量改动
async function requestDirectly(options = {}) { // 就是原来的业务,简述为代码如下:
const [, res] = await uni.request();
}
export async function request(options = {}) {
let [, res] = await requestDirectly(options);
if (res.data.code === 4001) {
post_login();
([, res] = await requestDirectly(options));
}
return res;
}
到这一步,基本上已经解决问题了,但其实还有一个问题:如果在登录失效的情况下,同时发起多个请求,会怎么样?
同时发起多个请求,那这几个请求可能都需要重新登录,于是都会去尝试登录 —— 但实际只,只需要有一个去尝试登录就可以了。
怎么处理呢?加一个锁变量,检查到锁变量就等待一定时间直到解锁,然后再尝试登录(失败则直接返回)。这个逻辑可以写在 post_login
中
let loginLock = false;async function waitLoginLock() {
return new Promise(resolve => {
if (!loginLock) { resolve(); }
const timer = setInterval(() => {
if (!loginLock) {
clearInterval(timer);
resolve();
}
// TODO 这里最好加一个超时判断
}, 200);
})
}
async function post_login() {
if (loginLock) {
await waitLoginLock();
return;
}
loginLock = true;
// TODO 这里是登录的逻辑
loginLock = false;
}
最后要处理一个遗留的问题,如果在 post_login
中调用 request
该怎么避免去尝试登录(否则可能造成无限递归)?
其实很简单……post_login
中去调用没尝试登录的那个 requestDirectly
就可以了。
不过 requestDirectly
是没 export 的(可能是为了保护),所以可以给 request
加一个参数(在 options
里或外都可以),根据这个参数来判断是否需要去尝试登录。
回答:
区分好哪些接口是需要要在登陆后马上进行请求的,可以将这些请求和登录请求包装到一个函数中。这样如果你登录状态失效后,直接调用这个函数。
以上是 谢谢大家,想请大家帮忙看看这个封装的请求怎样去修改? 的全部内容, 来源链接: utcz.com/p/932935.html