前言:
在我们开发的业务中,有时候不可避免的需要调用一些接口去轮询结果。(原因就是:比如说,我们借助第三方执行下单操作,但是我们需要知道下单后的状态,才能继续我们的下一步流程。因为第三方属于异步,所以不可能迅速获取到下单的状态,这个时候我们就需要轮询,一直到第三方返回具体的成功或者失败结果为止)。
技术栈:
vue3 + js + element plus
开发场景:
本文笔者将根据公司业务里真实的案例进行分析分享。
在调用下单操作的接口后,我们需要再调用轮询接口,去查询下单结果,如果下单成功,那么跳转进入收银台页面,如果失败,抛异常,提醒用户具体失败原因。
状态枚举:
1:结果查询中,未返回 2:下单成功 3:下单失败
轮询思路:
1.因第三方接口属于异步,所以具体返回结果时间不一定,这个时候就需要我们反复调用轮询接口,直到返回具体结果。
2.反复调用接口,要么使用定时器,要么使用递归。因定时器需要设置等待时间,但是具体写入等候时间,并不是很好的选择,同时还要考虑清除定时器的时机。在这里笔者更倾向于递归搭配延时器去操作,当上一次调用返回结果,再去调用下一次接口。
3.调用轮询接口的时候,需要给用户loading提示,但是这里得考虑不能每一次轮询,都用新的loading,否则就会页面一直闪烁,对于用户体验来说,是非常差的。笔者的思路是,针对轮询接口关闭请求拦截器里的loading,然后针对于轮询接口单独写入一个loading,然后轮询开始到轮询结束只用一个loading。
4.得考虑非正常情况下的失败情况,比如500的状态,不能让loading还在那里展示,当异常状态下使用try catch捕获,然后关闭loading,给出提示。
5.轮询次数不能无限制,假设第三方出问题了,一直不返回成功或者失败的状态。我们不可能一直调用接口,一直让页面loading加载。更不能期望于用户去大刷浏览器停止轮询操作。这个时候我们得设置具体的轮询次数,当到达指定的次数,还未获取到正确的结果,则中断轮询,提醒用户下单失败。
具体代码:
let queryLoading = null;
const queryDone = () => {
queryLoading.close();
queryNum = 0;
};
let queryNum = 0;
// 轮询下单结果
const queryResult = async (batchNo) => {
queryNum++;
if (queryNum > 30) {
ElMessage.error("下单失败");
queryDone();
return;
}
queryLoading ??
(queryLoading = ElLoading.service({
lock: true,
target: document.getElementsByClassName("router-view")[0],
text: "拼命加载中",
}));
try {
// 调用轮询订单状态接口
const queyRes = await queryChannelApplyCreateOrderResult({
batchNo,
bizType: params.value.bizType,
});
if (queyRes.data.handlerResult == 2) {
queryDone();
//跳转收银台页面
router.push({
path: "/goods-order/goods-payment",
query: { benefitOrderCode: JSON.stringify([queyRes.data.pcsOrderId]) },
});
} else if (queyRes.data.handlerResult == 3) {
queryDone();
ElMessage.error(queyRes.data.failReason);
return;
} else {
setTimeout(() => {
queryResult(batchNo);
}, 500);
}
} catch (error) {
queryDone();
}
};
tips:采用空合并运算符?? ,是为了能多次轮询只共用一个loading,优化用户体验感