完成对请求失败的逻辑补充

This commit is contained in:
wzhqwq 2021-08-09 23:49:43 +08:00
父節點 45df1f7409
當前提交 1b0e9d3042
共有 2 個檔案被更改,包括 45 行新增10 行删除

查看文件

@ -6,4 +6,4 @@
请根据`env.example.json`的格式按照自己的需求编写`env.json`,或者直接运行`cp env.example.json env.json`,**否则无法正确处理网络请求!**
### 网络请求的用法
引入./src/helper/axios.js(请以相对路径引入),尽情使用其中的post、get方法,它们会返回一个Promise(或可被视作Promise的AsyncFunction),且不会以抛出异常的方式通知网络错误,它会在服务器返回内容中插入属性`networkStatus`,**当值为200时才可以视作正确的服务器响应**,当值为-1时说明这是用户的网络错误**你不需要处理200以外的情况,代码能够发出弹窗并建议用户重试**
引入./src/helper/axios.js(请以相对路径引入),尽情使用其中的post、get方法,它们会返回一个Promise(或可被视作Promise的AsyncFunction),且为方便不会以抛出异常的方式通知网络错误。它会在服务器返回内容中插入属性`networkStatus`,**当值为200时才可以视作正确的服务器响应**,当值为-1时说明这是用户的网络错误**你不需要处理200以外的情况,代码能够发出弹窗并建议用户重试**

查看文件

@ -3,7 +3,7 @@ import { failed } from './alert';
import qs from 'qs';
import History from './history';
export function get(url) {
export function get(url, evoker) {
return send(
axios.get(url, {
headers: {
@ -11,10 +11,13 @@ export function get(url) {
"Allow-Control-Allow-Origin": "*"
}
}),
{ fn: () => get(url), identifier: 'get:' + url }
{
fetcher: () => get(url, evoker),
identifier: 'get:' + url + (evoker ? `@${evoker}` : '')
}
);
}
export function post(url, data) {
export function post(url, data, evoker) {
return send(
axios.post(url, qs.stringify(data), {
headers: {
@ -22,7 +25,10 @@ export function post(url, data) {
"Allow-Control-Allow-Origin": "*"
}
}),
{ fn: () => post(url, data), identifier: 'post:' + url + ' ' + JSON.stringify(data) }
{
fetcher: () => post(url, data, evoker),
identifier: 'post:' + url + (evoker ? `@${evoker}` : '')
}
);
}
@ -30,9 +36,19 @@ const waitToSend = [];
async function send(xhr, retryConf) {
if (waitToSend.length) {
if (waitToSend.every(retryConfItem => retryConfItem.identifier !== retryConf.identifier))
waitToSend.push(retryConf);
return;
// 等待列表不为空,说明需要等待用户决定是否重试,这也提高了目前请求的成功率
return await new Promise(res => {
if (waitToSend.every(retryConfItem => retryConfItem.identifier !== retryConf.identifier))
waitToSend.push({ ...retryConf, resolver: res });
else
waitToSend
.filter(retryConfItem => retryConfItem.identifier === retryConf.identifier)
.forEach(retryConfItem => {
retryConfItem.resolver({ networkStatus: -2, status: false });
retryConfItem.resolver = res;
});
// 虽然说理论上filter的结果只会有一个,但是还是要forEach一下,因为写起来方便hhhh
});
}
try {
const { data } = await xhr;
@ -49,11 +65,18 @@ async function send(xhr, retryConf) {
};
if (err?.response?.status === 401) {
History.force('/login');
feedWaitList(failData);
// 若是认证问题导致的错误,则没有必要继续后面等待的请求,且强制跳转后重试可能会导致已卸载的组件被更新
return failData;
}
if (!waitToSend.length) return failData;
waitToSend.push(retryConf);
// 等待列表不为空时弹框要么出现了要么就是在消失的路上,没有办法给予用户点击重试的机会,所以交由外部逻辑处理
// 注意,理论上带有时间戳的请求是不可以重试的,但是这里不做那方面考虑,如果未来有需要,可以自己实现一个刷新时间戳重试的逻辑
if (err.message === 'Network Error')
return await failed('您的设备似乎断网了,请检查网络后重试或刷新', flushWaitList) || failData;
return await failed('您的设备似乎断网了,或者服务器发生了问题,请检查网络后重试或刷新', flushWaitList) || failData;
if (!err?.response?.status)
return await failed('请求发生问题:' + err.message, flushWaitList) || failData;
if (err.response.status === 504)
@ -63,7 +86,19 @@ async function send(xhr, retryConf) {
}
function flushWaitList() {
let fns = waitToSend.map(item => item.fn);
let fns = waitToSend.map(
conf =>
async () => conf.resolver(await conf.fetcher())
);
waitToSend.splice(0, waitToSend.length);
fns.forEach(fn => fn());
}
function feedWaitList(data) {
let fns = waitToSend.map(
conf =>
() => conf.resolver(data)
);
waitToSend.splice(0, waitToSend.length);
fns.forEach(fn => fn());
}