- 先说一下业务场景:项目中有一个提交测算的按钮,点击后首先会进行页面切换,在新页面中需要持续从后端获取测算结果,根据结果进行不同的业务展示。
- 所以在新页面挂载之后我们需要做的是:以固定的时间间隔去调用后端所提供的接口。首先可以想到定时器
setInterval()
, 其次也可以根据递归➕setTimeout()
来实现!
- 递归➕
setTimeout()
- 首先定义轮询函数
polling
,返回一个Promise
对象,为控制轮询频率,传入参数delay
,默认为3000ms
- 在函数中请求后端接口,根据返回值进行不同的操作:1️⃣ 若请求成功但测算结果未出(
errno = 0
且isFinished = false
),采用定时器setTimeout
在delay
后再次递归调用轮询函数;2️⃣ 若请求成功且测算结果已出(errno = 0
且isFinished = true
),将调用resolve()
函数反对成功状态、promiseState
为后端返回的数据的Promise
对象; 3️⃣ 若请求失败,则返回失败状态的Promise
对象。
实现如下:
js
const polling = (delay = 3000) => new Promise((resolve, reject) => {
getUpdateRecord(resultIds).then((res: any = {}) => {
const { errno } = res;
const data = res?.data;
if (errno * 1 === 0 && data.isFinished === false) {
// 接口返回成功,但isFinished为false
timeout = setTimeout(() => {
resolve(polling(delay));
}, delay)
} else if (errno * 1 === 0 && data.isFinished === true) {
// 接口返回成功,但isFinished为true
resolve(res);
} else {
// 接口报错
reject(res);
}
});
});
调用:
js
useEffect(() => {
try {
polling(3000).then((res: any) => {
if (res?.errno * 1 === 0 && res?.data.isFinished === true) {
setFileUrl(res.data.fileUrl)
setIsLoading(false)
}
})
} catch (error: any) {
message.error('请求错误', error.errmsg)
}
return () => {
clearTimeout(timeout)
}
}, [])
同样的思路也可以采用 async/await
实现:
js
const polling = async (delay = 3000): Promise<any> => {
const res = await getUpdateRecord(resultIds);
const { errno } = res;
const data = res?.data;
if (errno * 1 === 0 && data.isFinished === false) {
// 接口返回成功,但isFinished为false
await new Promise(resolve => setTimeout(resolve, delay));
return polling(delay);
} else if (errno * 1 === 0 && data.isFinished === true) {
// 接口返回成功,但isFinished为true
return res;
} else {
// 接口报错
throw res;
}
};
-
setInterval()
使用
setInterval
可以避免递归调用带来的一些潜在问题,例如栈溢出风险,不过要注意在合适的时候清除定时器。
js
const polling = (delay = 3000) => {
return new Promise((resolve, reject) => {
const intervalId = setInterval(async () => {
const res = await getUpdateRecord(resultIds);
const { errno } = res;
const data = res?.data;
if (errno * 1 === 0 && data.isFinished === false) {
// 接口返回成功,但isFinished为false
return;
} else if (errno * 1 === 0 && data.isFinished === true) {
// 接口返回成功,但isFinished为true
clearInterval(intervalId);
resolve(res);
} else {
// 接口报错
clearInterval(intervalId);
reject(res);
}
}, delay);
});
};
今天的总结就到这啦~想起再改,欢迎指正