In the development process, we often have a situation where the function is called frequently, and if the function performs some DOM operation, then the browser will be very expensive in performance, thus affecting the user experience.
For example, in the following code, we call the function frequently during the scrolling process:
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta http-equiv=" x-UA-compatible "content="ie=edge"> <title>demo</title> <style type="text/ CSS "> body{ height:3000px; } </style> </head> <body> <script> function fn(){ console.log("invoke fn function"); }; document.body.onscroll = fn; </script> </body> </html>Copy the code
We open the browser, then open the console, and as we roll the mouse, the console frequently prints “Invoke FN Function”. For the sake of display, we do not involve dom manipulation, but in the actual development process, more scenes are dom manipulation, which will make your browser instantly feel like card, is there any way to limit the frequency of fn call, the answer is yes, we will change the above code as follows:
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta http-equiv=" x-UA-compatible "content="ie=edge"> <title>demo</title> <style type="text/ CSS "> body{ height:3000px; } </style> </head> <body> <script> function fn(){ console.log("invoke fn function"); }; document.body.onscroll = avoidShak(fn,300); Function avoidShak(fn,time){// Set let timer; return function(... Args){// Clear the last timer clearTimeout(timer); Let context = this; let _arguments = args; timer = setTimeout(()=>{ fn.apply(context,_arguments); },time); }; }; </script> </body> </html>Copy the code
We now find that the fn function is called less frequently when scrolling in the browser, and only once when we have a small pause of 300 milliseconds between scrolling, so we can reduce the frequency of function calls.
Its principle is actually very simple: 1. A closure is used to implement a timer variable, which is used to save the timer ID of the last function called; 2. Instead of calling the function directly, we need an interval between the two calls. If the time difference between the two calls is less than the value we passed, then we will clear the last call value; 3. When we call each time, we clear the timer ID of the last call, so as to ensure that if the interval time is less than the value we set, the last function will not be called, so as to achieve the effect of reducing the call frequency.
This practice of setting a timer to ensure that the event callback function can only be executed once in a certain period of time has a technical term in the javascript industry — anti-shock!
The above stabilization operation, we found, reduced the frequency of callback calls, but it had a slight flaw: If we keep firing, the callback will only be called once after we have stopped firing and reached the set interval, meaning that the callback will not be executed while we are firing, which in some cases may not be in line with actual business requirements. The actual business requirements may be, 1 reduce the trigger frequency; 2 But not for a long period of time. Ok, so now we need to function throttling to achieve!
Open the following code in a browser and zoom in horizontally to see what it looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>demo</title>
<style type="text/css">
*{
margin:0px;
padding:0px;
vertical-align:baseline;
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
.box{
width:1000px;
height: 500px;
background-color:#ccc;
color:#fff;
font-size:20px;
padding:15px 20px;
text-align:left;
}
</style>
</head>
<body>
<div class="box" id="box">i am a div box,please resize the browser horizontally!</div>
<script type="text/javascript">
let dom = document.getElementById('box');
function setWidth(){
let windowWidth = window.innerWidth;
if(windowWidth>=1000)return;
dom.style.width = windowWidth + 'px';
};
//采用防抖实现限制回调函数调用频率
function avoidShak(fn,time){
let timer;
return function(...args){
clearTimeout(timer);
let context = this;
let _arguments = args;
timer = setTimeout(()=>{
fn.apply(context,_arguments);
},time);
};
};
window.onresize = avoidShak(setWidth,300);
</script>
</body>
</html>Copy the code
Let’s start with the page requirements above: Open the page and do nothing while zooming in the browser, if the browser width is at least 1000, otherwise set the DOM width to the current browser width.
However, we found that the dom size was not updated during the zooming process. The dom width was updated to the browser width only after the zooming was stopped for a period of time, which was inconsistent with business requirements, so we changed the code as follows:
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta http-equiv=" x-UA-compatible "content="ie=edge"> <title>demo</title> <style type="text/ CSS "> *{ margin:0px; padding:0px; vertical-align:baseline; -webkit-box-sizing:border-box; -moz-box-sizing:border-box; box-sizing:border-box; } .box{ width:1000px; height: 500px; background-color:#ccc; color:#fff; font-size:20px; padding:15px 20px; text-align:left; } </style> </head> <body> <div class="box" id="box">i am a div box,please resize the browser horizontally! </div> <script type="text/javascript"> let dom = document.getElementById('box'); function setWidth(){ let windowWidth = window.innerWidth; if(windowWidth>=1000)return; dom.style.width = windowWidth + 'px'; }; Function ttrottle(fn,time){let isNeedInvoke = true; return function(... args){ if(! isNeedInvoke)return; let context = this; let _arguments = args; isNeedInvoke = false; setTimeout(()=>{ fn.apply(context,_arguments); isNeedInvoke = true; },time); }; }; window.onresize = ttrottle(setWidth,300); </script> </body> </html>Copy the code
We found that after this modification, the dom width was updated as we scaled, satisfying our business needs.
Ok, let’s briefly introduce what throttling is!
Throttling is just what it is in the name — limiting the frequency of function calls.
There are two main ways to achieve this:
- Method 1: time difference, the principle is nothing more than the time difference between two calls is less than the setting, then do not call, call on the contrary. The code is as follows:
Function ttrottle(fn,time){// let lastInvokeTime = new Date().gettime (); // Let currentInvokeTime; return function(... args){ currentInvokeTime = new Date().getTime(); if(currentInvokeTime - lastInvokeTime <= time)return; let context = this; let _arguments = args; lastInvokeTime = currentInvokeTime; fn.apply(context,_arguments); }; };Copy the code
- Method two: timer implementation, the principle is to set the time interval, if not up to the time interval, clear the last call callback timer ID. The code is as follows:
function ttrottle(fn,time){ let isNeedInvoke = true; return function(... args){ if(! isNeedInvoke)return; let context = this; let _arguments = args; isNeedInvoke = false; setTimeout(()=>{ fn.apply(context,_arguments); isNeedInvoke = true; },time); }; };Copy the code
Ok, so now you know what throttling is and how it works.
Finally, to summarize the difference between anti-shake and throttling:
-
The common point of anti-shake and throttling is to limit the frequency of callback calls;
-
The callback function is called only once for a period of time, the last time the event is triggered.
-
Throttling is called at intervals for a period of time;
Time is short, there is a mistake, welcome issue!