上述例子在开发中经常遇到,解决倒是很简单,但如何保证页面的完整性是一个问题。
?
我们需要请求三个接口,分别是接口a、接口b和接口c。请求过程中由于服务不稳定导致a成功了但b和c失败了。这种情况我们要么直接报错误信息,有点不友好;要么只使用a返回的数据,一般这种情况前端都会有默认值,页面自然而然展示出了默认信息,用户就会用错误的数据做出错误的决策,这是很严重的错误。为了解决上述问题,本文实现了一个容错机制,去尝试解决这个问题。
实现
重试方案
首先重试机制意味着我们需要多次请求失败接口,先解决这个问题,大概两张方案:
- socket
- 轮询
但是我们没有后端支持,socket直接干掉。那么只能通过轮询去尝试。先贴出轮询代码
export interface IParams {
maxCount?: number; // 最大轮询次数
intervalTime?: number; // 每次轮询增加时间长度
maxInterval?: number; // 最大轮询时间长度
}
export interface IProcessPayload<T> {
data: T;
count: number;
resolve?: (data: T) => void;
reject?: (err: any) => void;
}
/**
*
* error 失败
* process 继续轮询
* finish 结束轮询
*/
export type IProgressType = 'error' | 'process' | 'finish';
const defaultConfig = {
maxCount: 120,
intervalTime: 1000,
maxInterval: 1600,
};
export class PollingFun {
timeoutTimer: any;
cancelWhile: any;
constructor(private config: IParams = { maxCount: 120, intervalTime: 1000, maxInterval: 1600 }) {
this.config = { ...defaultConfig, ...config };
}
cancel() {
if (this.cancelWhile) {
this.cancelWhile();
this.cancelWhile = null;
}
if (this.timeoutTimer) {
clearTimeout(this.timeoutTimer);
}
}
pollingSingleTask = async <T>(onProgress: (data: IProcessPayload<T>) => IProgressType, ajaxFun: () => Promise<T>) => {
const { maxCount, intervalTime, maxInterval } = this.config;
let pollingCount = 0;
let stopPolling = false;
this.cancel();
this.cancelWhile = () => (stopPolling = true);
while (!stopPolling && pollingCount < maxCount) {
// 刚开始密集,后续间隔加长,最长1s。
let realIntervalTime = Math.floor(pollingCount / 10) * 200 + intervalTime; // eslint-disable-line
realIntervalTime = Math.min(realIntervalTime, maxInterval);
try {
const resData = await ajaxFun();
if (stopPolling) {
return Promise.reject('cancel');
}
const progressRes = onProgress({ data: resData, count: pollingCount });
switch (progressRes) {
case 'finish':
stopPolling = true;
return Promise.resolve(resData);
case 'error':
stopPolling = true;
return Promise.reject(resData);
default:
await new Promise(resolve => {
this.timeoutTimer = setTimeout(resolve, realIntervalTime);
});
break;
}
} catch (error) {
stopPolling = true;
return Promise.reject(error);
}
pollingCount += 1;
}
if (pollingCount >= maxCount) {
return Promise.reject('overMaxCount');
}
};
}
可以看到我们实现了一个轮询类,使用方式也很简单,只需要每次new一个实例,然后调用对应的方法即可。轮询方法需要两个参数,一个是轮询处理函数,其接受一个参数,会携带本次轮询的数据,我们只需要对数据做判断,然后返回相应的数据处理轮询。
const pollInstance = new PollingFun();
pollInstance.pollingSingleTask(process, ajaxFun);
重试机制
由上述背景我们可以知道,请求成功意味着所有请求都返回了结果,脑袋一转,想到了Promise.all,瞬间解决了一半的问题。我们只需要把每个请求函数包裹成轮询的方式,然后等着拿值就行,上手开干!
type AjaxFun<T> = [() => Promise<T>, (data: IProcessPayload<T>) => IProgressType, IParams];
const createPromise = <T>(ajaxFunArr: AjaxFun<T>[]) => {
return ajaxFunArr.map(item => {
const [ajaxFn, onProcess, options] = item;
const pollInstance = new PollingFun(options);
return new Promise((resolve, reject) => {
pollInstance.cancel();
pollInstance
.pollingSingleTask(payload => onProcess({ ...payload, resolve, reject }), ajaxFn)
.catch(err => reject(err));
});
});
};
export const ajaxCatch = async <T>(ajaxFunArr: AjaxFun<T>[]) => {
const wrapAjaxFunArr = await createPromise(ajaxFunArr);
return Promise.all([...wrapAjaxFunArr])
.then(res => ({
status: true,
data: res,
}))
.catch(err => ({
status: false,
data: err,
}));
};
可以看到,我们封装了一个ajax处理函数,这个函数需要一个数组类型参数,每个数组子值需要提供有三个值,分别是当前请求函数、控制轮询状态的函数以及轮询初始化的值。看着还不错 试试效果
const wake = async val => {
console.log(val);
return await val;
};
const onProcess = pay => {
const { data, resolve, count } = pay;
if (data === 'q2' && count === 3) {
resolve(data);
return 'finish';
}
if (data === 'q2') {
return 'process';
}
return 'finish';
};
export const getData = async () => {
const res = await ajaxCatch([
[() => wake('q1'), onProcess, { maxCount: 5 }],
[() => wake('q2'), onProcess, { maxCount: 5 }],
[() => wake('q3'), onProcess, { maxCount: 5 }],
]);
console.log(res, 'cdc');
};
完美达到我们需要的效果!
总结
上述实现可以多次重试失败接口,并统一返回结果,用户也不会看到页面闪烁等问题,同时还可以当做轮询函数来使用,可谓一举两得!所以嘛,遇到问题不要慌,慢慢分析一步步解决!
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!