1, Essence
- The essence is that an event is triggered frequently, resulting in frame loss or performance waste. Anti shake or throttling is used to reduce the frequency of executing events.
- The essence of anti chattering throttling is to preserve the upper scope after closure execution.
2, Anti shake:
1. Concept
- If this kind of event is triggered again within a certain time, the timer of the previous event will be cleared and re timed. If it is not triggered again within the specified time, this event will be executed.
- Simple understanding: prevent jitter to avoid mistaking one event trigger for multiple times
- If the scroll event is not triggered again within xxms, the function is executed
- If the rolling event is triggered again within 200ms, the current timing will be cancelled and the timing will be restarted
2. Most basic version
Note that this is just an example of scroll. In fact, it saves a little more money
- p.addEventListener('scroll', debounce(fn, 1000));
- Execute debounce(fn, 1000)), and bind the internal function to the event. Because of the closure, the debounce scope is retained in the internal function, that is, timer
- For the second trigger event, the last timer will be cleared and re timed whether it is time or not.
- Note that setTimeOut will implicitly lose this point
<style> .p { width: 600px; height: 100px; overflow-y: auto; } .son { width: 600px; height: 500px; background-color: pink; } </style> </head> <div class="p"> <div class="son"></div> </div> <body> <script> function debounce(fn, wait) { let timer = null; return function() { console.log(this); //this is p if (timer) { clearTimeout(timer); } timer = setTimeout(fn, wait); } } function fn() { console.log(this); //this is window console.log(233); } let p = document.querySelector('.p'); p.addEventListener('scroll', debounce(fn, 1000)); </script> </body>
At this time, note that this points to fn as window
3. Fix this pointing version
Solve the loss of this in the above situation and bind it back with apply.
function debounce(fn, wait ) { let timer = null; return function() { let context = this; let args = arguments; if (timer) { clearTimeout(timer); } timer = setTimeout(() => { fn.apply(context, args) }, wait); } } function fn() { console.log(this); //This is the p node console.log(233); }
4. Point to the line version immediately
-
What is immediate execution?
-
Suppose there is a user who seems to have a pain. He has been pulling the scroll bar up and down to play. At this time, you will find that the above anti shake can not be triggered once, which is not in line with the reality. So let the first time you trigger an event, execute the event first, and then anti shake to ensure that you can execute an event callback at least once.
-
immediate represents whether to choose to execute immediately. In fact, it is the leading edge anti shake concept. The execution action cycle starts and the action is executed after the trailing edge anti shake event cycle ends. Borrow one my dear friend Throttling also has corresponding.
-
This is actually done using timer==null
function debounce(fn, wait, immediate) { let timer = null; return function() { let context = this; let args = arguments; if (immediate) { if (timer === null) { fn.apply(context, args);//Execute immediately for the first time } else { clearTimeout(timer); } timer = setTimeout(() => { timer = null;//It is not triggered again during this period. Set it to null to prepare for the next trigger }, wait); } else {//Not immediately if (timer) { clearTimeout(timer); } timer = setTimeout(() => { fn.apply(context, args) }, wait); } } }
5. Merge optimized version
- One thing to note here is the order of callnow - > cleartimeout - > setTimeout
- In fact, there is another version that maxTime must be executed once within the maximum time. You can refer to a great God Writing method , the general idea is to use Date to record time.
function debounce(fn, wait, immediate) { let timer = null; return function() { let context = this; let args = arguments; let callNow = immediate && !timer;//Execute immediately if(callNow){ fn.apply(context,args); } clearTimeout(timer); timer = setTimeout(waitfun,wait); //Execute immediately = > timer = = null = > callnow = true //Immediate = false = > wait = > execute fn function waitfun() { timer = null;//Ensure that the second wait is executed immediately if (!immediate) { //It is not executed immediately after the waiting time fn fn.apply(context, args); } } } }
6. Application scenario (avoid jitter to avoid mistaking one event trigger for multiple times)
- Search intelligent prompt: it is impossible for users to send an ajax request every time they press it. They can wait for a period of time without input before initiating the request.
- Text editor saves in real time.
- resize when resizing the browser window
3, Throttling:
1. Concept
The same event can only be triggered once within a certain time
If the same event is triggered in a large number in a short time, after the function is executed once, the function will not work for the specified time period until it takes effect again.
2. Compare versions using two dates
function throttle(fn, wait) { //A function that is opened regularly like a control valve, that is, after the function is executed once, it will temporarily fail in a certain period of time, and then reactivate after this period of time let timeout; let startTime = new Date(); return function() { let context = this; let args = arguments; let curTime = new Date(); // If the specified trigger interval is reached, the handler is triggered if (curTime - startTime >= wait) { fn.apply(context, args); startTime = curTime; } }; }
3. timer comparison version
function throttle(fn, wait) { let timer = null; let startTime = new Date(); return function() { let context = this; let args = arguments; if (timer === null) { //timer alive means it's still cooling timer = setTimeout(() => { fn.apply(context, args);//Put this line before setTimeOut and click OK timer = null; }, wait) } }; }
4. Application scenario (control the flow of water. Control the frequency of events)
- The verification code can only be obtained once every 60 seconds
- scroll event, calculate the location information every second (for lazy loading)
- The browser plays events and calculates progress information every second
4, Summary
- Anti shake to prevent shaking and multiple accidental injury triggering.
- Throttling, flow control, only performed once per unit time.
- Principle: using closure property.
5, Let's have a question
How to implement lazy loading:)
Principle:
- img's src should not be set or set to a small thumbnail (data src = 'your picture path' is set to simulate ajax)
- Monitor the scroll event, get the distance between the picture and the browser viewport, and load the picture when it arrives.
- Here, we use the element.getBoundingClientRect()api to obtain the distance between the picture and the top of the browser viewport. This has compatibility problems and can be replaced by other properties of scoll. MDN
schematic diagram:
Simple implementation:
<div class="pic"> <img class='lazyImg' data-src="./can-use-is-ok.jpg" data-index='1'> </div> <div class="pic"> <img class='lazyImg' data-src="./can-use-is-ok.jpg" data-index='2'> </div> <div class="pic"> <img class='lazyImg' data-src="./can-use-is-ok.jpg" data-index='3'> </div> <div class="pic"> <img class='lazyImg ' data-src="./can-use-is-ok.jpg" data-index='4'> </div> <div class="pic"> <img class='lazyImg ' data-src="./can-use-is-ok.jpg" data-index='5'> </div> <div class="pic"> <img class='lazyImg ' data-src="./can-use-is-ok.jpg" data-index='6'> </div>
function fn() { var viewHeight = window.innerHeight //Get browser viewport height var imgs = document.querySelectorAll('.lazyImg ') imgs.forEach((cur_img, index) => { if (cur_img.src !== '') { return; //The picture has been loaded } var rect = cur_img.getBoundingClientRect() //Gets the position of the element relative to the viewport if (rect.bottom > 0 && rect.top < viewHeight) { cur_img.src = cur_img.dataset.src cur_img.removeAttribute('data-src') } }) } function throttle(fn, wait) { var timer = null; return function() { var _this = this var args = arguments if (timer === null) { fn.apply(_this, args) timer = setTimeout(() => { timer = null }, wait) } } } fn()//Execute once first window.addEventListener('scroll', throttle(fn, 1000))