Application scenarios

Anti – shake and throttling are aimed atHigh frequency triggerAnd the emergence ofControl trigger frequencyIs a performance optimization solution.

For example: mouse movement event onMousemove, scroll bar event onScroll, window size change event onresize, listen to input box onInput event…

Debounce

I’ll start with a common scenario: the remote search function of the input box, where you need to listen to the request interface after the onINPUT event is triggered. A problem arises when the user enters the search content one word at a time and requests the interface once the content of the input box changes. In fact, only the last input is what the user wants to search, which leads to a waste of server resources.

At this point, the transformation is carried out, the goal is: the user input process does not trigger the request, stop input 400 milliseconds after the trigger. That’s when debounce came in.

Version 1(delayed execution): New events are triggered in the cycle, the old timer is cleared, and the new timer is reset.

	
// The policy is to set a period when the event is triggered to delay the execution of the action, if the period is triggered again, reset the period until the end of the period, the execution of the action.
var debounce = (fn, wait) = > {
	let timer, // Timer ID
    	timeStamp=0.// Last execution time
	context,// Execution 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();
 		// Compares the current time with the last execution time difference with the set interval
		if(now-timeStamp < wait){
			console.log('reset',now);
			clean();  // Clear the timer
			run();    // Reset the new timer from the current time
		}else{
			console.log('set',now);
			run();    // The last timer was executed. Set a new timer
		}
        // Record the last execution timetimeStamp=now; }}/ / use
document.getElementById('inp').addEventListener(
'input'.// Executing debounce returns a closure that triggers the execution of the function
  debounce(function() {
    console.log(this.value)
  }, 400));Copy the code

See the effect

Version 2: new events are triggered within the cycle, and the latest trigger time is recorded. After the current cycle ends, whether there is a trigger within the current cycle is judged. If there is a delay device is set, and so on. (This version does not clear timers)

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){ // Check whether the current cycle is triggered
				console.log('debounce reset',timerInterval-interval);
				startTimeStamp=now;
				run(wait-interval);  // Reset the remaining time of the timer
			}else{
            	// No action is triggered within the period
				fn.apply(context,args);
				clearTimeout(timer);
				timer=null;
			}
			
		},timerInterval);
	}
 
	return function(){
		context=this;
		args=arguments;
		let now = (new Date()).getTime();
		startTimeStamp=now; // Record the last trigger time
 
		if(! timer){console.log('debounce set',wait);
			run(wait);    // The last timer was executed. Set a new timer}}}Copy the code

Version 3(Leading-edge Execution): Added immediate execution option based on version 2

When events are triggered in rapid succession, the action is executed only once, leading edge debounce, at the beginning of the cycle.

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 start time of the timer is reset, so interval is shorter than timerInterval
				console.log('debounce reset',timerInterval-interval);
				startTimeStamp=now;
				run(wait-interval);  // Reset the remaining time of the timer
			}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; // Record the last trigger time
 
		if(! timer){console.log('debounce set',wait);
            		// Execute immediately
			if(immediate) {
				fn.apply(context,args);
			}
			run(wait);    // The last timer was executed. Set a new timer}}}Copy the code

Throttling

The command is executed only once in a period. If a new event is triggered, the command is not executed. After the cycle ends, an event is triggered to start a new cycle. Throttling strategies are also divided into leading edge and delay.

Latency: throttling

For example: Listen for browser scroll bar scroll events

// Trigger the scroll event to print the scroll bar position
onscroll = function() {
	console.log(this.scrollY)
}
Copy the code

And then something happens and it’s triggered very frequently, and I press itThe arrowTriggered nine times.

This is where throttling comes in if the processing logic is messy and can be a burden on browser performance.

// During the timer, the trigger is executed only after the timer ends
var throttling = (fn, wait) = > {
	let timer;
	let context, args;
 
	let run = () = > {
		timer=setTimeout(() = >{
        		// Execute after the cycle ends
			fn.apply(context,args);
			clearTimeout(timer);
			timer=null;
		},wait);
	}
 
	return function () {
		context=this;
		args=arguments;
		if(! timer){console.log("The beginning of the cycle");
			run();
		}else{
			console.log("Throttled"); }}}/ / use
onscroll = throttling(function() {console.log(this.scrollY)},200)
Copy the code

Leading edge throttling

// Add a leading edge based on the previous version

var throttling = (fn, wait, immediate=false) = > {
	let timer, timeStamp=0;
	let context, args;
 
	let run = () = > {
		timer=setTimeout(() = >{
        		// Execute after the cycle ends
			if(! immediate){ fn.apply(context,args); }clearTimeout(timer);
			timer=null;
		},wait);
	}
 
	return function () {
		context=this;
		args=arguments;
		if(! timer){console.log("The beginning of the cycle");
            		// leading-edge execution
			if(immediate){
				fn.apply(context,args);
			}
			run();
		}else{
			console.log("Throttled"); }}}Copy the code