欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

uniapp中定时任务异步开启时如何防止泄露

程序员文章站 2022-05-18 16:37:26
...

setTimeout与setInterval选择

项目里要用到实时刷新,在一个tarbar的页面,几秒一次请求后台数据。这里比较常见做法是setInterval,setInterval会每次定时时间到立即执行一次,不管上一次是否已经执行结束。但是考虑到几个方面:后台接口的限流,ip连接数的限制,网络延迟,担心使用setInterval在极端情况下有大量冗余请求(上一次请求未结束未失败就发起新请求,,请求的执行时间和定时任务间隔时间大量重叠,最终在极短时间内,重复刷新)。所以选用setTimeout,在函数执行结束的回调里,再发起一次setTimeout。比如:

let millSecond = 1000;
function solve(){
	...
	business code ...
	...
	setTimeout(solve,1000);
	return solve;
}
function start(){
	setTimeout(solve(),1000);
}

页面onHide与onShow优化带来的并发问题

为了优化性能以及降低不必要请求,希望在tarbar页面onHide的时候,停止请求,而onShow时继续请求。网上的做法大部分是,保存定时器对象,在对应方法中结束,但是使用了setTimeout这会带来一些并发问题,如下

var _this;
export default {
	onLoad() {
		_this = this;
	},
	onShow(){
		_this.timer = setTimeout(_this.refresh(),_this.refreshMillSecond);
	},
	onHide() {
		if(_this.timer){
			clearTimeout(_this.timer);
		}
	},
	methods: {
		refresh(){
			uni.request({
		        ...
		    }).then(res => {
		        _this.timer = setTimeout(_this.refresh,1000);
		    }).catch(error => {
		    _this.timer = setTimeout(_this.refresh,1000);
		  })
			return _this.refresh;
		}
	}
}

最终测试,这种情况下,可能js也是有线程屏障,导致的结果是,有时onHide并没有关闭掉定时器。主要问题是

_this.timer = setTimeout(_this.refresh,1000);

因为定时器是在请求回调,也就是异步里去重启,这里和onHide关闭有并发问题,如果异步回调在onHide之后执行,就会导致定时任务泄露,没有关闭

优化方法

加入一个全局变量,代表当前定时任务是否还能继续的开关,在onHide时,关闭开关。定时任务每次要开启新的定时任务,必须先读以下开关,确保开关没关闭才能开启,这样通过在定时开始时添加一次检查,不会造成泄露

var _this;
export default {
	data(){
		return {
			timerStart : false
		},
	},
	onLoad() {
		_this = this;
	},
	onShow(){
		_this.timerStart = true;
		setTimeout(_this.refresh(),_this.refreshMillSecond);
	},
	onHide() {
		_this.timerStart = false;
	},
	methods: {
		refresh(){
		   if(_this.timerStart){
				uni.request({
			        ...
			    }).then(res => {
			        _this.timer = setTimeout(_this.refresh,1000);
			    }).catch(error => {
			    _this.timer = setTimeout(_this.refresh,1000);
			  })
		  }
			return _this.refresh;
		}
	}
}

父子组件问题

现在uniapp里,子组件已经不支持onShow与onHide、onLoad这些页面生命周期事件了。而且

$refs方式在app好像也用不了

网上查阅资料测试后发现,可以通过

this.$emit和this.$on

做全局事件的传递,在父组件中触发事件

//父组件中
export default {
	onShow(){
		this.$emit('aaaa')
	}
}

在子组件中监听

//子组件
export default {
	mounted(){
		this.$on('aaaa',this.onShow);
	},
	methods:{
		onShow(){}
	}
}

在父组件的onShow事件中,调用,触发一个叫aaaa的全局事件给子组件,子组件在mounted函数里,通过this.$on(‘aaaa’,callback)来完成对父组件onShow事件的监听,来调用自己的事件处理,但是有个问题,如果你父组件有多个子组件,这个事件会发送多次(多少个子组件会发送多少次)
uniapp中定时任务异步开启时如何防止泄露

相关标签: 客户端