The original link
preface
Generally for listening to some intensive keyboard, mouse, gesture events need to interact with the back-end request, modify DOM, tremble, throttling is very necessary.
Image stabilization
Usage scenarios
- Keyword Remote search drop-down box
- resize
For this type of operation, you want to get the final keyword entered by the user, determine the drag size, and then interact with the server. In order to reduce the pressure on the server and avoid wasting resources on the server, it is necessary to prevent shaking.
case
- Input box anti – shake
// Record the time
let last = new Date().getTime();
// Simulate an Ajax request
function ajax(content) {
const d = new Date().getTime();
const span = d - last;
console.log(`${content}interval${span}ms`);
last = d;
}
const noActionInput = document.getElementById('noAction');
noActionInput.addEventListener('keyup'.function(e) {
ajax(e.target.value);
});
Copy the code
- Not being
You can see that too many requests are being sent for too many intermediate states.
/** * Generally uses closures to store and privatize timer 'timer' ** when called again within 'delay' time to clear the unexecuted timer * retimer * @param fn * @param delay * @returns {Function} */
function debounce(fn, delay) {
let timer = null;
return function() {
// all intermediate states are cleared
timer && clearTimeout(timer);
// Just need the final state, execute
timer = setTimeout((a)= > fn.apply(this.arguments), delay);
};
}
const debounceInput = document.getElementById('debounce');
let debounceAjax = debounce(ajax, 100);
debounceInput.addEventListener('keyup'.function(e) {
debounceAjax(e.target.value);
});
Copy the code
- After being
It can be found:
- If the input is slow, about every
delay 100ms
Execute once; - If the input is too fast, it indicates that the user is continuously typing. The callback is executed after the user finishes typing and slows down.
The throttle
Usage scenarios
- Sliding scroll bar
- Shooting games shoot bullets
- Flow rate of water from faucet
For some continuous events, in order to show smooth transitions, we also need to care about intermediate states. But attenuating the frequency of intensive events is still a performance optimization killer.
errata
Two very common errors, so popular that I can’t help correcting them.
// Time stamp version
function throttleError1(fn, delay) {
let lastTime = 0;
return function () {
const now = Date.now();
const space = now - lastTime; // Time interval, the first time will be a large value
if (space > delay) {
lastTime = now;
fn.apply(this.arguments); }}; }}// Timer version
function throttleError2(fn, delay) {
let timer;
return function () {
if(! timer) { timer = setTimeout((a)= > {
timer = null;
fn.apply(this.arguments); }, delay); }}; }Copy the code
Both versions have problems. First, assume delay=100ms, and assume that timers are executed on time.
- The time stamp version
- Because for the first time
now - lastTime === now
This value is very large, the first 0ms immediately executed, the user within 0-100ms to execute the interaction is invalid, if the user stayed in 99ms, the last lost. - For example, if you want to set the style by the height of the scroll bar from the top, you can’t handle the scroll bar from 0 to 100px in 99ms.
-
PS: time-stamped version, with an application scenario, to prevent repeated submissions within a certain period of time.
-
The timer version
- The first 0ms will not be executed immediately with a 100ms delay, like the first shot takes 100ms for the bullet to come out.
- Smart readers, as you might imagine, can combine the two to solve the problem.
- First 0ms immediate execution without delay;
- Get the last state to ensure the last execution.
case
- Visually continuous adjustment while scrolling the slider
dom
* @param fn * @param delay * @returns {Function} */
function throttle(fn, delay) {
let timer, lastTime;
return function() {
const now = Date.now();
const space = now - lastTime; // Interval time
if( lastTime && space < delay ) { // In response to the user's last operation
// It is not the first time that the timer has not reached the time.
timer && clearTimeout(timer);
// Reset the timer
timer = setTimeout((a)= > {
lastTime = Date.now(); // Don't forget to record the time
fn.apply(this.arguments);
}, delay);
return;
}
// Time is up
lastTime = now;
fn.apply(this.arguments);
};
}
const throttleAjax = throttle(ajax, 100);
window.addEventListener('scroll'.function() {
const top = document.body.scrollTop || document.documentElement.scrollTop;
throttleAjax('scrollTop: ' + top);
});
Copy the code
- Before the throttle
The pullback was too dense. (PS: It is often heard that scroll has its own throttle, which means that it is triggered once at about 16ms per frame.)
- After the throttle
As you can see, whether you ski slowly or fast, it’s like a timed trigger.
- Careful readers may notice that if the interaction is stuck at 199ms and the timer is executed at 300ms, with an interval of about 200ms, the timer delay should not be set to the original
delay
.
* @param fn * @param delay * @returns {Function} */ Function throttle(fn, delay) { let timer, lastTime; return function() { const now = Date.now(); const space = now - lastTime; / / time interval between the if (lastTime && space < delay) {/ / the last time in response to user action / / not for the first time, haven't to time, remove the timer, timing again. timer && clearTimeout(timer); // Reset the timer timer = setTimeout(() => {- lastTime = now; // This was wrong before, thanks for pointing it out in the comments section
+ lastTime = Date.now(); // Don't forget to record the time
fn.apply(this, arguments);
- }, delay);
+ }, delay - space);return; } // time = now; fn.apply(this, arguments);+ // If the timer is not cleared, there will be redundant intermediate execution
+ timer && clearTimeout(timer);
};
}
Copy the code
- If you forget to clear at the end
- The final result
-
The actual production is achieved by using LoDash’s mature and reliable anti-shake and throttling capabilities.
-
Lodash effect
-
Trailing controls the execution of 99ms. Trailing controls the execution of 100ms. MaxWait controls the execution of 99ms.
-
Here are some common ways to use Lodash.
- Achieve similar
scroll
Comes with a frame16ms
Effect:throttle(fn, /* wait = undefined */)
It’s actually used internallyrequestAnimationFrame
. - Very common, sending requests to prevent repeated submissions, such as the first click to execute,
500ms
All clicks inside will not be executed:debounce(fn, 500, { leading: true, trailing: false })
- For example, a
dom
Cleared,debounced.cancel()
To cancel the trailing call to avoid a dom error. - And so on.
conclusion
Both anti-shake and throttling use closures to achieve internal data acquisition and maintenance. Anti – shake is easier to understand, throttling needs to be thought about a little bit. There is still a difference between the two, do not make a mistake again, paste the problem code. Anti – shake and throttling are indispensable for performance optimization of frequent DOM events.
reference
- In this paper, the demo
- 7 minutes to understand the throttling, anti-shaking and application scenarios of JS