谢谢大家,想请大家帮忙看看这个封装的请求怎样去修改?

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;

}


接下来处理问题本身。问题本身要处理的是两个分支:

  1. 如果是 post_login 调用 request,不处理 4001 之类的错误(这个在问题中没明确描述)
  2. 如果是其他调用 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 中去掉尝试登录的逻辑,就是要封装的内容。所以这里有两种处理方式

  1. request 中移除尝试登录的代码,把这个事情交给一个新的外层封装 requestWithLogin 去干
  2. 直接在 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

回到顶部