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