Function anti-shake of JavaScript common skills

Execute the event after a period of time, and if it fires again within this period, continue to wait for the same amount of time.

Anti-shake is still used in many scenarios in actual development, such as real-time search of lists. Delayed execution, it is easy to think of using setTimeout to achieve.

Basic Edition

function debounce(func, wait) {
    let timer;
    
    return function () {
        timer && clearTimeout(timer);
        timer = setTimeout(func, wait);
    };
}

have a test

// Do a test with an input box
// html
<input id="input" />
<div id="text"></div>

// js
input.oninput = debounce(() => {
    text.innerHTML = input.value;
}, 1000);


Ok. There seems to be no problem. However, we found that the input.value is used when getting the value of the input above. In fact, the oninput event should have a parameter that is event, which can be obtained directly by using event.target.value.

// Do a test with an input box
// html
<input id="input" />
<div id="text"></div>

// js
input.oninput = debounce((e) => {
    text.innerHTML = e.target.value; // Uncaught TypeError: Cannot read property 'target' of undefined
}, 1000);

Unexpectedly, an error is reported, because the debounce function returns a new function, and does not bind the this value of the original function to the new function, nor pass parameters to the new function. Let's change it.

A version that handles this and arguments

function debounce(func, wait) {
    let timer;
    
    return function (...args) {
        timer && clearTimeout(timer);
        
        timer = setTimeout(() => {
            func.apply(this, args);
        }, wait);
    };
}

At this time, there is a new requirement, which needs to be executed at the beginning of the timing, not at the end. Similarly, the original logic remains unchanged, but a new logic that is executed immediately needs to be added. Think about our needs: execute immediately, then not trigger a second time for a fixed period of time. After the code is executed immediately, a variable flag (initial value is true) can be set to record whether the last execution time has exceeded wait until now. If it exceeds, the flag is set to true, which means that it can be executed again.

function debounce(func, wait, immediate) {
    let timer;

    return function (...args) {
        timer && clearTimeout(timer);

        if (immediate) {
            // timer is the flag we use
            if (!timer) {
                func.apply(this, args);
            }
            
            // set timer to null after wait s of code execution
            // The next time the current function is executed, the judgment can be triggered !timer === true
            timer = setTimeout(() => {
                timer = null;
            }, wait);
        } else {
            timer = setTimeout(() => {
                func.apply(this, args);
            }, wait);
        }
    };
}

Of course, we can also add cancel, pending and other methods like lodash.

function debounce(func, wait, immediate) {
    let timer;
    
    const debounced = function (...args) {
        timer && clearTimeout(timer);
    
        if (immediate) {
            if (!timer) {
                func.apply(this, args);
            }
            
            timer = setTimeout(() => {
                timer = null;
            }, wait);
        } else {
            timer = setTimeout(() => {
                func.apply(this, args);
                timer = null;
            }, wait);
        }
    };
    
    debounced.cancel = function() {
        timer && clearTimeout(timer);
    }
    
    debounced.pending = function() {
        return !!timer;
    }
    
    return debounced;
}

Related:
Anti-shake for daily learning of JavaScript
Throttling of daily learning of JavaScript

The complete code for the above code

Tags: Javascript Front-end

Posted by Ang3l0fDeath on Sat, 21 May 2022 16:02:10 +0300