起因
(百度翻遍都没找到,只能自己解决了)
对于长时间请求,使用interval不稳定,需要用settimeout结合递归来实现请求发送完成后再请求的效果
时隔两个月的补充,因为又栽倒在了同一个问题上
- 模拟代码
var myIntervalTimer = null
const myInterval = (fn, delay) => {
clearInterval(myIntervalTimer)
myIntervalTimer = setTimeout(() => {
fn().then(() => {
console.log('自定义延时器执行')
myInterval(fn, delay)
})
}, delay)
}
myInterval(async () => {
console.log('模拟请求');
await new Promise((resolve) => setTimeout(resolve, 5000))
}, 2000)
setTimeout(() => {
clearInterval(myIntervalTimer)
}, 10000);
理解到在执行最后一次请求时,删除定时器已经晚了,请求已经执行
结果还是要把方法置空或者设置一个标志,现在封装成对象方便以后再用
class myInterval {
constructor(fn, time) {
this.end = false
// 定义一个递归函数,持续调用定时器
var execute = async (fn, time) => {
await fn();
if (!this.end) {
// 在函数执行完毕后才再起用定时器
setTimeout(function () {
console.log('自定义延时器执行')
execute(fn, time);
}, time);
}
};
execute(fn, time);
}
clear() {
this.end = true
}
}
let myIntervalTimer = new myInterval(async () => {
console.log('模拟请求');
await new Promise((resolve) => setTimeout(resolve, 5000))
}, 2000)
setTimeout(() => {
myIntervalTimer.clear()
}, 10000);
首先是对clearTimeout和clearInterval的理解,来自mdn:
- 都是根据id清除
clearTimeout:取消了建立的定时器(我的理解是在任务放入任务队列前进行销毁)clearInterval:取消设置的重复定时任务。
所以有两种引发清除失败的情况:
- 对于
setInterval,如果重复赋值,离开组件再清除的话,只会清除最后一次的id,所以要在设置定时器前使用clearInterval。 - 对于
setTimeout模拟setInterval,销毁最后一次定时器后也会因为回调继续执行,因为这里用到的回调是异步的,删除定时器前,then回调就已经推入队列了,并且匿名函数闭包持有回调函数,导致轮询继续,解决方案是清除定时器时将方法置空。 - 补充:正确的做法是使用同步,总之理解原理就好找bug了
原始代码:
const updateData = () => {
clearTimeout(timer);
this.timer = setTimeout(async () => {
const res = await request.request.getCloudDevice();
this.tableData = res.data?.data;
this.total = this.tableData.length;
this.tableList = this.tableData;
this.searchItem(this.inputKey);
updateData();
}, 5000);
};
updateData();
beforeDestroy() {
this.timer && clearInterval(this.timer);
this.timer = null;
},
修复代码:
let timer;
let updateData = () => {
timer = setTimeout(async () => {
const res = await request.request.getCloudDevice();
this.tableData = res.data?.data;
this.total = this.tableData.length;
this.tableList = this.tableData;
this.searchItem(this.inputKey);
updateData();
}, 5000);
};
updateData();
//再通过事件监听,监听到 组件销毁 后,再执行关闭计时器。
this.$once('hook:beforeDestroy', () => {
clearTimeout(timer);
updateData = null;
});