In depth understanding of js anti shake and throttling

Posted by jaku78 on Fri, 24 Dec 2021 21:08:04 +0100

preface

In the process of front-end development, we often need to bind some continuously triggered events, such as resize, scroll, mousemove, etc., but sometimes we don't want to execute functions so frequently in the process of continuously triggering events.

How do we usually solve this problem? Generally speaking, anti shake and throttling are better solutions.

debounce

The so-called anti shake means that the function can only be executed once in n seconds after the event is triggered. If the event is triggered again in n seconds, the function execution time will be recalculated.

Non immediate version:

function debounce(func, wait) {
    let timeout;
    return function () {
    	//let context = this; let args = arguments;
        if (timeout) clearTimeout(timeout);
        
        timeout = setTimeout(() => {
            func.apply(this, arguments)
        }, wait);
    }
}

The non immediate execution version means that the function will not execute immediately after the event is triggered, but will execute after n seconds. If the event is triggered again within n seconds, the function execution time will be recalculated.

The code of the above anti shake function also needs to pay attention to the transfer of this and parameters
If the timer does not use the arrow function, you also need to store the DOM elements and parameters with context and arg and rebind them to the fn function, because this in the arrow function points to the current context,

Execute now:

function debounce(func,wait) {
    let timeout;
    return function () {

        if (timeout) clearTimeout(timeout);

        let callNow = !timeout;
        timeout = setTimeout(() => {
            timeout = null;
        }, wait)

        if (callNow) func.apply(this, arguments)
    }
}

The immediate execution version means that the function will execute immediately after the event is triggered, and then the function can continue to execute only if the event is not triggered within n seconds.

In the development process, we need to decide which version of the anti shake function we need to use according to different scenarios. Generally speaking, the above anti shake functions can meet the needs of most scenarios. However, we can also combine the anti shake function of non immediate execution version and immediate execution version to realize the anti shake function of the final double sword combined version.

Twin swords combined version

/**
 * @desc Function anti shake
 * @param func function
 * @param wait Milliseconds of delayed execution
 * @param immediate true The table executes immediately, and the false table executes non immediately
 */
function debounce(func,wait,immediate) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

throttle

Throttling means that events are triggered continuously, but the function is executed only once in n seconds.
Throttling will dilute the execution frequency of the function. Generally, there are two ways to implement throttling: timestamp version and timer version.

Timestamp version

function throttle(func, wait) {
    let previous = 0;
    return function() {
        let now = Date.now();
        if (now - previous > wait) {
            func.apply(this, arguments);
            previous = now;
        }
    }
}

Timer version

function throttle(func, wait) {
    let timeout;
    return function() {
       // let context = this;
        //let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(this, arguments)
            }, wait)
        }

    }
}

It can be seen that in the process of continuously triggering events, the function will not be executed immediately, and it will be executed every 1s. After stopping triggering events, the function will be executed again.

It should be easy to find that the difference between the throttling function of the timestamp version and the timer version is that the function trigger of the timestamp version is at the beginning of the time period, while the function trigger of the timer version is at the end of the time period.

Similarly, we can combine the throttling function of timestamp version and timer version to realize the throttling function of double sword version.

Double swords combined version:

/**
 * @desc Function throttling
 * @param func function
 * @param wait Milliseconds of delayed execution
 * @param type 1 Table timestamp version, 2 table timer version
 */
function throttle(func, wait ,type) {
    if(type===1){
        let previous = 0;
    }else if(type===2){
        let timeout;
    }
    return function() {
        let context = this;
        let args = arguments;
        if(type===1){
            let now = Date.now();

            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }else if(type===2){
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }
    }
}

Topics: Javascript