In the process of front-end development, we often need to bind events that are constantly firing, such as Resize, Scroll, mousemove, etc., but sometimes we don’t want to execute functions that frequently during events that are constantly firing.
How do we usually solve this situation? Generally speaking, anti – shake and throttling are better solutions.
Let’s take a look at what happens when a function is executed frequently while the event continues to fire.
The code in the HTML file is as follows
<div id="content"
style="height:150px; line-height:150px; text-align:center; color: #fff; background-color:#ccc; font-size:80px;"></div>
<script>
let num = 1;
const content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
Copy the code
In the code above, the div element is bound to the Mousemove event, which is continuously triggered when the mouse moves over the div (gray) area, causing the function to execute frequently. Results the following
As you can see, the frequency with which the function is executed without doing anything else causes the data on the page to change very quickly. So, let’s take a look at how anti-shake and throttling can solve this problem.
Debounce
The so-called shaking prevention means that the function is executed n seconds after the event is triggered. If the event is triggered within N seconds, the function execution time will be recalculated.
Anti – shake function is divided into non – immediate execution version and immediate execution version.
Non-immediate version:
function debounce(func, wait) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, wait); }}Copy the code
The non-immediate version means that the function is not executed immediately after the event is fired, but n seconds later. If the event is fired within n seconds, the function execution time is recalculated.
We are still using the mousemove event binding example above, which we can use with the anti-shake function above
content.onmousemove = debounce(count,1000);
Copy the code
Results the following
As you can see, the function does not execute until 1 second after the event is triggered, and if I fire the event again within 1 second after the event is triggered, the function execution time is recalculated.
Another thing to note in the code above is the passing of this and the argument
const context = this;
const args = [...arguments];
Copy the code
The code for the stabilization function uses these two lines of code to get this and the arguments so that debounce eventually returns a function that points to this unchanged and still accepts e.
Immediate Release:
function debounce(func,wait) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if(timeout) clearTimeout(timeout); const callNow = ! timeout; timeout =setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
}
Copy the code
The immediate execution version means that the function is executed immediately after the event is fired, and then the effect of the function cannot be continued until the event is fired for n seconds.
Use the same method, the effect is as follows
In the development process, we need to decide which version of the anti-shake function we need to use according to different scenarios, generally speaking, the above anti-shake function can meet most of the requirements of the scene. However, we can also combine the non-immediate version and immediate version of the anti-shake function, to achieve the final version of the double sword anti-shake function.
Double Sword combined round edition:
/** * @desc * @param func * @paramwaitNumber of milliseconds * @param immediatetrueThe table is executed immediately,falseThe table does not execute */ immediatelyfunction debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
if(immediate) { const callNow = ! timeout; timeout =setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
else {
timeout = setTimeout(() => {
func.apply(context, args)
}, wait); }}}Copy the code
Throttle
Throttling refers to firing events continuously but executing the function only once in n seconds. Throttling dilutes the execution frequency of the function.
For throttling, there are generally two ways to achieve, respectively, the timestamp version and the timer version.
Timestamp version:
function throttle(func, wait) {
var previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) { func.apply(context, args); previous = now; }}}Copy the code
Use as follows
content.onmousemove = throttle(count,1000);
Copy the code
Results the following
As you can see, the function is executed immediately and every 1s during a continuous event firing.
Timer version:
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if(! timeout) { timeout =setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)}}}Copy the code
Use the same way, the effect is as follows
As you can see, the function is not executed immediately during a continuous event firing, and is executed every 1s, and again after the event firing is stopped.
It should be easy to see that the difference between the timestamp and timer throttling functions is that the timestamp function fires at the beginning of a period, while the timer function fires at the end of a period.
Similarly, we can combine the timestamp and timer throttling functions to achieve the dual-sword throttling function.
Double Sword combined round edition:
/** * @desc function throttle * @param func function * @paramwaitNumber of milliseconds delayed execution * @paramtype1 table timestamp version, 2 table timer version */function throttle(func, wait ,type) {
if(type= = = 1) {let previous = 0;
}else if(type= = = 2) {let timeout;
}
return function() {
let context = this;
let args = arguments;
if(type= = = 1) {let now = Date.now();
if (now - previous > wait) { func.apply(context, args); previous = now; }}else if(type= = = 2) {if(! timeout) { timeout =setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)}}}}Copy the code
Reference article:
Github.com/mqyqingfeng…
Github.com/mqyqingfeng…