I got a request this week – fuzzy match for the input box. Listen for the input event and call the interface from the input value. However, the backend said no, the amount of data in this interface is very large, the frequency of calling the interface in this way is too high, and there is no need to call the user input, as long as the user stops the input at the moment to switch the interface. Oh? Why does this scene sound so much like stabilization?
So what exactly is anti-shake? We must have seen the left and right sides of the middle of the advertising space, in the web page scrolling, advertising space to keep in the middle of the screen, it is necessary to constantly calculate the position, if not limited, in the visual advertising space is like “shaking”. To prevent this situation, it is called anti – shake!
What is the principle of anti – shake? I always think the example circulating on the Internet is very vivid: when we are taking the elevator, if someone comes by at this moment, we will press the open button and wait out of politeness. When this person enters the elevator, we are just about to close the door, and find that someone comes by again! Again, if the elevator had infinite space, we would have to wait forever… Of course, human patience is limited! So we set a time, like 10 seconds, and if no one shows up in 10 seconds, we close the elevator door.
In technical terms, a function is fired multiple times in a given interval, but only once.
The simplest version of the code implementation:
function debounce(fn, delay) {
let timer = null;
return function() {
const context = this;
const args = arguments;
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout((a)= > {
fn.apply(context, args);
}, delay);
};
}
Copy the code
Fn is the function to be buffed, delay is the set delay, debounce returns an anonymous function that forms a closure and maintains a private timer internally. What we always trigger is the returned anonymous function, and the timer will return an Id value to the timer. If the anonymous function is triggered again within the delay interval, the timer will be cleared and the timer will start again.
Of course, the simple version will certainly not meet everyday needs, such as those that may need to be implemented immediately for the first time, so it needs to be changed slightly:
function debounce(fn, delay, immediate) {
let timer = null;
return function() {
const context = this;
const args = arguments;
timer && clearTimeout(timer);
if(immediate) {
constdoNow = ! timer; timer = setTimeout((a)= > {
timer = null;
}, delay);
doNow && fn.apply(context, args);
}
else {
timer = setTimeout((a)= >{ fn.apply(context, args); }, delay); }}; }Copy the code
There is an additional parameter, immediate, that distinguishes whether execution is required immediately. The rest of the logic is almost identical to that of the simplified version, except where the judgment is executed immediately:
constdoNow = ! timer; timer = setTimeout((a)= > {
timer = null;
}, delay);
doNow && fn.apply(context, args);
Copy the code
The value of the doNow variable is! Timer, only! The fn function is executed only when timer is true. The first time it is executed, the timer starts with null, so fn is executed immediately. In subsequent non-first execution cases, wait for delay time before triggering fn again. Attention! Different from the simple version, the simple version is triggered several times within a certain time, the execution of the last time. The immediate execution version does not execute the last time and needs to be triggered again.
The buffeting function may have a return value, we also want to do compatibility:
function debounce(fn, delay, immediate) {
let timer = null;
return function() {
const context = this;
const args = arguments;
let result = undefined;
timer && clearTimeout(timer);
if (immediate) {
constdoNow = ! timer; timer = setTimeout((a)= > {
timer = null;
}, delay);
if(doNow) { result = fn.apply(context, args); }}else {
timer = setTimeout((a)= > {
fn.apply(context, args);
}, delay);
}
return result;
};
}
Copy the code
There is a drawback to this implementation, however, because except for the first immediate execution, all execution is performed in a timer, which is asynchronous execution, and the return value is undefined.
For asynchrony, we could also return a Promise:
function debounce(fn, delay, immediate) {
let timer = null;
return function() {
const context = this;
const args = arguments;
return new Promise((resolve, reject) = > {
timer && clearTimeout(timer);
if (immediate) {
constdoNow = ! timer; timer = setTimeout((a)= > {
timer = null;
}, delay);
doNow && resolve(fn.apply(context, args));
}
else {
timer = setTimeout((a)= >{ resolve(fn.apply(context, args)); }, delay); }}); }; }Copy the code
As such, whenever fn is executed, it must get the return value! This is the ultimate in anti-shock!
Next time we’ll talk about the brother of anti-shock – throttle-throttle for front-end performance optimization.