防抖(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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 软件激活成功教程教程2[通俗易懂]

    软件激活成功教程教程2[通俗易懂]第四章–调试器及相关工具入门在写这章之前,我看了一下看雪以往的教程。本来想参考一下,可忽然发现,写这样的一章,是一件非常愚蠢的事情,因为我觉的关于这些工具的使用教程。看雪教程中已经写的够详细的了,我并不认为你会看不懂。所以我不想做浪费时间的人,本章就此搁浅。推荐看《CrackTutorial2001》,推荐看《看雪论坛精华一、二、三、四》,推荐看《加密与解密--软件保护技术及完全解决方案》,

    2022年6月18日
    36
  • JDBC连接数据库6个步骤

    JDBC连接数据库6个步骤JDBC连接数据库,创建一个以JDBC连接数据库的程序,包含7个步骤:   首先准备JDBC所需的四个参数(user,password,url,driverClass)(1)user用户名(2)password密码(3)URL定义了连接数据库时的协议、子协议、数据源标识,它们之间用冒号隔开。  书写形式:  协议:子协议:数据源标识       协议:在JDBC中总是以jdb…

    2022年7月12日
    13
  • 电脑爱好者必须懂得的电脑简单英语单词[通俗易懂]

    电脑爱好者必须懂得的电脑简单英语单词[通俗易懂]使用电脑必懂的53个英文单词和缩写!  ·PC:个人计算机PersonalComputer  ·CPU:中央处理器CentralProcessingUnit  ·CPUFan:中央处理器的“散热器”(Fan)  ·MB:主机板MotherBoard  ·RAM:内存RandomAccessMemory,以PC-代号划分规格,如PC-133,PC-1066,PC-27…

    2022年6月3日
    35
  • dell服务器显示器fre,戴尔发布Gaming 24/27游戏显示器新品 支持144/155Hz FreeSync

    dell服务器显示器fre,戴尔发布Gaming 24/27游戏显示器新品 支持144/155Hz FreeSync访问购买页面:具体说来,24英寸机型覆有防眩光涂层(硬度3H),长宽比为16:9、支持1670万色,水平/垂直可视角度为160/170°。此外还有LEDedgelight、ComfortView、戴尔显示管理器、且兼容VESA壁挂安装。接口方面,该机型提供了2×HDMI1.4、DisplayPort、一个USB3.0上联+两个USB3.0下联、耳机…

    2022年5月8日
    39
  • 【LeetCode】Linked List Cycle

    【LeetCode】Linked List Cycle

    2022年1月6日
    50
  • GridView编辑删除操作

    GridView编辑删除操作

    2021年12月15日
    33

发表回复

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

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