Principle of concept
In project development, there are scenarios where events are frequently triggered, such as: 2. Mouse events onMousemove, onMouseDown, onmouseup 3. Keyup triggered by input box entry, keyDown……
To optimize such scenarios, we usually use two schemes: throttling: Events are executed every n seconds during high-frequency triggering. Anti – shake: After the event is triggered at a high frequency, the event is executed once every n seconds.
According to the above principles, the difference between the two is that the timing of the callback function is different: the timing of the callback function of the throttling function is in the high-frequency trigger, while the timing of the callback function of the anti-shake function is after the high-frequency trigger.
implementation
Based on the above principles, we can write the first version of a simple implementation:
HTML page code
<body>
<div id="root" style="width: 500px; height: 200px; background: yellow;"></div>
<script>
function func(e) {
console.log('mousemve'.this, e);
}
/ / throttling
function throttle(){}
/ / image stabilization
function debounce(){}
root.addEventListener('mousemove', throttle(func, 1000));
</script>
</body>
Copy the code
Image stabilization
* Delete the existing timer every time the event triggers, set a new timer, wait n seconds to trigger the event callback **/
function debounce(fn, wait){
let timer;
return function(. args){
let ctx = this;
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(ctx, args);
timer = null;
}, wait)
}
}
Copy the code
Effect:
The throttle
The throttling function can be implemented in two ways: 1. Timestamp; 2. Timer
Timestamp implementation
/** * if the interval is longer than the wait time, execute **/
function throttle(fn, wait){
let previous = 0;
return function(. args){
let ctx = this;
let now = +new Date(a);if(now - previous > wait){ fn.apply(ctx, args); previous = now; }}}Copy the code
Effect:
Timer implementation
* Each time the event is triggered, determine whether the timer exists * if not, set the timer, * if yes, wait for the timer to complete the execution of the reset timer **/
function throttle(fn, wait){
let timer;
return function(. args){
let ctx = this;
if(! timer){ timer =setTimeout(function(){
timer = null;
fn.apply(ctx, args)
}, wait)
}
}
}
Copy the code
Effect:
To improve the
Through the above processing, we have been able to control the execution frequency and timing of high-frequency events. However, through observation, we find that the above code still has the following problems: In the anti-shake function, the callback function is not executed when the mouse enters the color block for the first time. 2. In the timestamp throttling, the callback function will not be executed after the mouse leaves the color block for 1 second
In view of the above problems, we make the following adjustments to the function:
Anti-shake takes an option object that controls whether the callback function is executed when triggered for the first time. Option is an object for extending other control functions
/** * thread: *! If timer is true, the callback is triggered for the first time
function debounce(fn, wait, option={}){
let timer;
let { leading } = option;
return function(. args){
let ctx = this;
if(! timer && leading ===true) {// Execute the callback for the first time
fn.apply(ctx, args);
}
clearTimeout(timer);
timer = setTimeout(function(){
fn.apply(ctx, args);
timer = null;
}, wait)
}
}
Copy the code
Throttling we integrate the timestamp implementation with the timer implementation to complete the callback execution after the initial trigger and mouse exit
/** * calculate the time between the next event execution: Wait - (now-previous) * If the time before the next execution <=0, the callback is executed and the timer is cleared. * If the execution time is not reached and the timer is not available, the timer is set to ensure that the mouse is off the color block before the last event callback **/ is executed
function throttle(fn, wait) {
let previous = 0;
let timer;
return function (. args) {
let ctx = this;
let now = +new Date(a);let remaining = wait - (now - previous);
if (remaining <= 0) {
if(timer){
clearTimeout(timer);
timer = null;
}
fn.apply(ctx, args);
previous = now;
} else if(! timer) { timer =setTimeout(function () {
previous = +new Date(a); timer =null;
fn.apply(ctx, args);
}, remaining)
}
}
}
Copy the code
To optimize the
In the throttling function, we can also control the execution of the first and trailing triggered callbacks by passing the Option configuration parameter. We reuse the improved throttling function, which performs the header and tail callbacks by default. Drawing lessons from previous examples, we specify two variables that control the execution of the head and tail callback functions: when trailing === false, we cancel the execution of the function callback that was triggered for the first time. When trailing === false, we cancel the execution of the trailing callback
/***** thread: 1, trailing === false, the trailing callback is not executed. 2. Leading ==== false: The initial callback is not executed. By default, the first callback is triggered when remaining <=. If the previous is set to now, remaining === wait will trigger the timer on time. If the previous is set to Now, remaining === wait will trigger the timer on time. When remaining > 0 is mandatory, the function is triggered for the first time, and the function will not respond until the timer is executed for the second time. Set timer = null, previous = 0 note: When the timer is executed, if previous is not set to 0, when the mouse is out of the target area and the wait time is greater than the set AWIT value, the throttling function is triggered again because previous has not been released. If previous is false, remaining > 0 cannot be forcibly set. Therefore, the initial callback is still performed, resulting in a BUG ****/
function throttle(fn, wait, option) {
let previous = 0;
let timer;
let { leading, trailing } = option;
return function (. args) {
let ctx = this;
let now = +new Date(a);if(! previous && leading ===false) previous = now; // Remaining > 0 by setting previous
let remaining = wait - (now - previous);
if (remaining <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(ctx, args);
previous = now;
} else if(! timer && trailing ! = =false) { / / when the trailing! If == false, a timer is added and a trailing callback is performed
timer = setTimeout(function () {
previous = leading === false ? 0 : +new Date(a);// Sets previous to 0
timer = null;
fn.apply(ctx, args);
}, remaining)
}
}
}
Copy the code
Trailing: false and leading: false cannot be set at the same time.
When two attributes are set at the same time, the callback is triggered for the first time. Due to leading === false and previous = 0, remaining > 0 does not execute the function. When the callback is triggered for the second time, previous = now has been set for the first time, so! If previous is true, the value of remaining cannot be forcibly set until the code enters the if branch of Remaining <= 0 when the interval between triggering a callback is longer than the set wait. The trailing === false timer cannot be started and previous = 0 cannot be initialized. The trailing === false timer cannot be started and previous = 0 cannot be initialized. A BUG.
The last
Above, we have done a brief understanding of throttling and anti – shake. Be sure to correct any errors. Thank you very much!