防抖(debounce) 和 节流(throttling)「建议收藏」

防抖(debounce) 和 节流(throttling)「建议收藏」防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。在给DOM绑定事件时,有些事件我们是无法控制触发频率的。如鼠标移动事件onmousemove,滚动滚动条事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件会被高频触发。如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。在实时检查输入时,如果我们绑定onkeyup事件发请求去…

大家好,又见面了,我是你们的朋友全栈君。

防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。 在给DOM绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件onmousemove, 滚动滚动条事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件会被高频触发。 如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。 在实时检查输入时,如果我们绑定onkeyup事件发请求去服务端检查,用户输入过程中,事件的触发频率也会很高,会导致大量的请求发出,响应速度会大大跟不上触发。

针对此类快速连续触发和不可控的高频触发问题,debounce 和 throttling 给出了两种解决策略;

debounce,去抖动。策略是当事件被触发时,设定一个周期延迟执行动作,若期间又被触发,则重新设定周期,直到周期结束,执行动作。 这是debounce的基本思想,在后期又扩展了前缘debounce,即执行动作在前,然后设定周期,周期内有事件被触发,不执行动作,且周期重新设定。

延迟debounce,示意图:

防抖(debounce) 和 节流(throttling)「建议收藏」

前缘debounce, 示意图

防抖(debounce) 和 节流(throttling)「建议收藏」

 

debounce的特点是当事件快速连续不断触发时,动作只会执行一次。 延迟debounce,是在周期结束时执行,前缘debounce,是在周期开始时执行。但当触发有间断,且间断大于我们设定的时间间隔时,动作就会有多次执行。

debounce 的实现:

版本1:  周期内有新事件触发,清除旧定时器,重置新定时器;这种方法,需要高频的创建定时器。

// 暴力版: 定时器期间,有新操作时,清空旧定时器,重设新定时器
var debounce = (fn, wait) => {
	let timer, timeStamp=0;
	let context, args;

	let run = ()=>{
		timer= setTimeout(()=>{
			fn.apply(context,args);
		},wait);
	}
	let clean = () => {
		clearTimeout(timer);
	}

	return function(){
		context=this;
		args=arguments;
		let now = (new Date()).getTime();

		if(now-timeStamp < wait){
			console.log('reset',now);
			clean();  // clear running timer 
			run();    // reset new timer from current time
		}else{
			console.log('set',now);
			run();    // last timer alreay executed, set a new timer
		}
		timeStamp=now;

	}

}

版本2: 周期内有新事件触发时,重置定时器开始时间撮,定时器执行时,判断开始时间撮,若开始时间撮被推后,重新设定延时定时器。

/ 优化版: 定时器执行时,判断start time 是否向后推迟了,若是,设置延迟定时器
var debounce = (fn, wait) => {
	let timer, startTimeStamp=0;
	let context, args;

	let run = (timerInterval)=>{
		timer= setTimeout(()=>{
			let now = (new Date()).getTime();
			let interval=now-startTimeStamp
			if(interval<timerInterval){ // the timer start time has been reset, so the interval is less than timerInterval
				console.log('debounce reset',timerInterval-interval);
				startTimeStamp=now;
				run(wait-interval);  // reset timer for left time 
			}else{
				fn.apply(context,args);
				clearTimeout(timer);
				timer=null;
			}
			
		},timerInterval);
	}

	return function(){
		context=this;
		args=arguments;
		let now = (new Date()).getTime();
		startTimeStamp=now;

		if(!timer){
			console.log('debounce set',wait);
			run(wait);    // last timer alreay executed, set a new timer
		}
		
	}

}

版本3: 在版本2基础上增加是否立即执行选项:

// 增加前缘触发功能
var debounce = (fn, wait, immediate=false) => {
	let timer, startTimeStamp=0;
	let context, args;

	let run = (timerInterval)=>{
		timer= setTimeout(()=>{
			let now = (new Date()).getTime();
			let interval=now-startTimeStamp
			if(interval<timerInterval){ // the timer start time has been reset,so the interval is less than timerInterval
				console.log('debounce reset',timerInterval-interval);
				startTimeStamp=now;
				run(wait-interval);  // reset timer for left time 
			}else{
				if(!immediate){
					fn.apply(context,args);
				}
				clearTimeout(timer);
				timer=null;
			}
			
		},timerInterval);
	}

	return function(){
		context=this;
		args=arguments;
		let now = (new Date()).getTime();
		startTimeStamp=now; // set timer start time

		if(!timer){
			console.log('debounce set',wait);
			if(immediate) {
				fn.apply(context,args);
			}
			run(wait);    // last timer alreay executed, set a new timer
		}
		
	}

}

 

throttling,节流的策略是,固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。 节流策略也分前缘和延迟两种。与debounce类似,延迟是指 周期结束后执行动作,前缘是指执行动作后再开始周期。

延迟throttling示意图:

防抖(debounce) 和 节流(throttling)「建议收藏」

前缘throttling 示意图:

防抖(debounce) 和 节流(throttling)「建议收藏」

throttling的特点在连续高频触发事件时,动作会被定期执行,响应平滑。

throttling 的实现:

版本1: 简单版

简单版: 定时器期间,只执行最后一次操作
var throttling = (fn, wait) => {
	let timer;
	let context, args;

	let run = () => {
		timer=setTimeout(()=>{
			fn.apply(context,args);
			clearTimeout(timer);
			timer=null;
		},wait);
	}

	return function () {
		context=this;
		args=arguments;
		if(!timer){
			console.log("throttle, set");
			run();
		}else{
			console.log("throttle, ignore");
		}
	}

}

版本2: 增加前缘选项:(考虑情况较简单,复杂情况可参考underscope 的_.throttle)

/// 增加前缘
var throttling = (fn, wait, immediate) => {
	let timer, timeStamp=0;
	let context, args;

	let run = () => {
		timer=setTimeout(()=>{
			if(!immediate){
				fn.apply(context,args);
			}
			clearTimeout(timer);
			timer=null;
		},wait);
	}

	return function () {
		context=this;
		args=arguments;
		if(!timer){
			console.log("throttle, set");
			if(immediate){
				fn.apply(context,args);
			}
			run();
		}else{
			console.log("throttle, ignore");
		}
	}

}

 

debounce和throttling 各有特点,在不同 的场景要根据需求合理的选择策略。如果事件触发是高频但是有停顿时,可以选择debounce; 在事件连续不断高频触发时,只能选择throttling,因为debounce可能会导致动作只被执行一次,界面出现跳跃。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/151540.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 尤克里里谱bm和弦_尤克里里基础曲谱

    尤克里里谱bm和弦_尤克里里基础曲谱Ukulele即夏威夷小吉他,在港台等地一般译作乌克丽丽,在大陆一般习惯称为尤克里里,是一种四弦夏威夷的拨弦乐器,发明于葡萄牙盛行于夏威夷,归属在吉他乐器一族。下面是小编收集整理的尤克里里入门基础范文,欢迎借鉴参考。…尤克里里是一种四弦夏威夷的拨弦乐器,发明于葡萄牙盛行于夏威夷,归属在吉他乐器一族。那么下面是小编收集整理的尤克里里的调音方法及注意事项,一起来看看吧。尤克里里的调音方法1、认…

    2022年8月21日
  • CentOS7配置LLDP服务

    CentOS7配置LLDP服务作者:BK运维团队成员官方链接:https://vincentbernat.github.io/lldpd/usage.html官方图片:什么是lldpdLLDP可以让你准确的知道服务器所连接的交换机端口号。LLDP是一种工业标准协议,用于取代EDP或CDP等专用链路层协议。LLDP的目标是提供一个inter-vendor兼容机制,向相邻网络设备发送链路层通知。lldpd是一个L…

    2022年5月28日
  • 【值得收藏】一份非常完整的Mysql规范

    做一个积极的人编码、改bug、提升自己我有一个乐园,面向编程,春暖花开!本文转载在,在原有内容基础上结合阿里巴巴Java开发手册中Mysql数据库章节的介绍,加上自己的理解和说明,整理而成。涉及的内容较多,建议收藏后仔细阅读!一、数据库命名规范数据库的表名、字段名必须使用小写字母或数字(不要使用驼峰哦),并禁止出现数字开头,禁止两个下划线中间只出现数字。正例:aliyun _ …

    2022年2月28日
  • 真肝,整理了一周的Spring面试大全【含答案】,吊打Java面试官[通俗易懂]

    真肝,整理了一周的Spring面试大全【含答案】,吊打Java面试官[通俗易懂]对于Java的小伙伴来说,Spring是面试的必问环节,我研究Spring多年,甚至我的网名都叫SpringMeng。最新整理的数据结构和算法的值得收藏:❤️肝完了,一天掌握数据结构和算法面试题,吊打面试官,建议收藏❤️小孟肝了一周,继续给大家整理Spring的系列,帮你offer收到手疼。废话不多话,直接上干货。Q1什么是spring?Spring是个java企业级应用的开源开发框架。Spring主要用来.

    2022年5月30日
  • charles导致mac无法上网_charles抓不到请求

    charles导致mac无法上网_charles抓不到请求前言charles关闭后,发现网页突然打开了,那大概率是设置了代理,但明明已经关闭了charles,这是由于mac网络偏好设置中,使用的是手动代理,将其改为自动即可解决方法1打开网络偏好设置,

    2022年7月29日
  • pycharm打包后的exe运行不起_pygame打包成exe闪退

    pycharm打包后的exe运行不起_pygame打包成exe闪退在网上找了很多解决方法,有人说是需要添加环境变量,有人说是需要把import的包放到要打包的文件的同一文件夹下,折腾了一晚上一一排查后都不行,最后加了一个python交流群,在大佬的指导下解决了问题,为了感谢python交流群的帮助以及大佬的无私奉献,这里将群号列出帮忙打个广告,群号:786788101,大佬qq号:3124265576,再次感谢。言归正传,解决方法是在报错目录下寻找pyinstaller这个文件夹,如果没有的话找到你的python安装目录,将C:\ProgramFiles(x86)

    2022年8月26日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号