Anti shake and throttling

Anti shake and throttling

Anti shake

The same function is triggered multiple times in a short time, and only the first / last time is executed. For example: a kitten is shivering with cold. Press it hard and it won't tremble. You can press and hold it before it shakes, or after it shakes for a long time

Common anti shake

/**
 * Common implementation
 * Function anti shake [only the first / last time when the function is called frequently]
 * @param {Function} func Functions requiring anti shake
 * @param {Number} [duration=1000] Duration of anti shake [how long does the click need anti shake]
 */
function debounce (func, duration = 1000) {
    let timer = null;
    return function (...args) {
        /*if (timer!== null)*/ clearTimeout(timer);
        timer = setTimeout(() => {
            clearTimeout(timer);
        	func.apply(this, args);
        }, delay);
    }
}

If you want to bind this to the anti shake function, use the following method

Bind this

function mClick() {
    console.log('I was called!');
}

debounce(mClick).bind(this);

Anti shake with configuration item

/**
 * Function anti shake
 * @param {Function} func Functions requiring anti shake
 * @param {Boolean} [first=true] Whether anti shake for the first time
 * @param {Number} [duration=1000] Duration of anti shake
 * @returns 
 */
function debounce(func, first = true, duration = 1000){
    // Declare an empty timer id to prepare for anti shake
	let timer = null;
	// The return function is because this anti shake function may be bound / assigned to other functions
	return function (...args) {
		first && !timer && func.apply(this, args);
		// There is no need to judge whether the timer exists. Passing in an incorrect ID to clearTimeout() will not have any impact; No exceptions will be thrown.
		clearTimeout(timer);
		timer = setTimeout(() => {
            clearTimeout(timer);
			// This function is called if it is not the first time
			!first && func.apply(this, args);
			// Prepare for the next anti shake
			timer = null;
		}, duration);
	};
}

Cancelable anti shake

/**
 * Function anti shake
 * @param {Function} func Functions requiring anti shake
 * @param {Boolean} [first=true] Whether anti shake for the first time
 * @param {Number} [duration=1000] Duration of anti shake
 * @returns 
 */
function debounce(func, first = true, duration = 1000){
    // Declare an empty timer id to prepare for anti shake
	let timer = null;
    let debounced = function (...args) {
		first && !timer && func.apply(this, args);
		// There is no need to judge whether the timer exists. Passing in an incorrect ID to clearTimeout() will not have any impact; No exceptions will be thrown.
		clearTimeout(timer);
		timer = setTimeout(() => {
            clearTimeout(timer);
			// This function is called if it is not the first time
			!first && func.apply(this, args);
			// Prepare for the next anti shake
			timer = null;
		}, duration);
	};
    debounced.cancel = function () {
        clearTimeout(timer);
        // Prepare for the next anti shake
        timer = null;
    }
	// The return function is because this anti shake function may be bound / assigned to other functions
	return debounced;
}

throttle

Functions are only allowed to execute once in a period of time

Usage scenario: the scroll bar may scroll very fast, but it can't be captured by the naked eye, so the function calls that can't be captured by the naked eye are unnecessary, so it saves money and performance.

Timestamp throttling

It is executed for the first time, but not for the last time during the duration

It will be executed immediately when the event is triggered for the first time [the interval between the event binding function and the real trigger event is generally greater than duration]. After each duration, the function can be triggered and executed again.

/**
 * Function throttling
 * @param {Function} func Functions requiring throttling
 * @param {Number} [duration=1000] Duration of throttling
 */
function throttle(func, duration = 1000) {
    let prev = 0;
    return function (...args) {
        const now = Date.now();
        // The length of time the function is called twice is greater than the throttling length
        if (now - prev > duration) {
            func.apply(this, args);
            prev = Date.now();
        }
    }
}

Bind this

function mScroll () {
    console.log('I'm sliding!');
}
throttle(mScroll).bind(this);

Timer throttle

The first trigger will not be executed, but will be executed after the duration. After the last trigger is stopped, the function will be executed again.

/**
 * Function throttling
 * @param {Function} func Functions that need to be throttled
 * @param {Number} [duration=1000] Throttling duration
 */
function throttle(func, duration = 1000) {
    let timer = null;
    return function (...args) {
        // If the timer is not completed, the function is called again and does not take effect
        if (!timer) {
            timer = setTimeout(() => {
                clearTimeout(timer);
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    }
}

Timestamp + timer throttling

Both the first and last time. [the two are used alternately. The first time is a timestamp, the second time is a timer, the third time is a timestamp, and the fourth time is a timer... Ideally, this is the case, but sometimes it may not be the case due to the impact of message queue processing of the timer.]

/**
 * Function throttling
 * @param {Function} func Functions that need to be throttled
 * @param {Number} [duration=1000] Throttling duration
 */
function throttle (func, duration = 1000) {
    let timer, prev = 0;
    return function (...args) {
        const now = Date.now();
        // Solve the first non execution
        if (now - prev > duration) {
            // When the execution of a timer ends, clear the timer
            if (timer){
                clearTimeout(timer);
                // If the timer exceeds the duration and still does not execute the function [affected by other things], cancel the timer and execute the function immediately
           		timer = null;
            }
            func.apply(this, args);
            prev = Date.now();
        } else if (!timer) { // Solve the last non execution
            // Used when the duration of two calls is less than duration
            timer = setTimeout(() => {
                prev = Date.now();
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    };
}

Throttling with configuration items

/**
 * Function throttling
 * @param {Function} func Functions that need to be throttled
 * @param {Object} option Throttling configuration item
 * option = { 
 * 		leading: false, // Prohibit first execution
 * 		trailing: false // Prohibit last execution
 *	}
 *		leading And tracking cannot be false at the same time [both false, throttling failure]
 * @param {Number} [duration=1000] Throttling duration
 */
function throttle (func, option, duration = 1000) {
    !option && (option = { leading: true, trailing: true });
    let timer, prev = 0;
    return function (...args) {
        const now = Date.now();
        if (!(option.leading || prev)) {
            prev = now;
        }
        // Solve the first non execution
        if (now - prev > duration) {
            // When the execution of a timer ends, clear the timer
            if (timer){
                clearTimeout(timer);
                // If the timer exceeds the duration and still does not execute the function [affected by other things], cancel the timer and execute the function immediately
           		timer = null;
            }
            func.apply(this, args);
            prev = Date.now();
        } else if (!timer && option.trailing) { // Solve the last non execution
            // Used when the duration of two calls is less than duration
            timer = setTimeout(() => {
                clearTimeout(timer);
                prev = Date.now();
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    };
}

Cancelable throttling

/**
 * Function throttling
 * @param {Function} func Functions that need to be throttled
 * @param {Object} option Throttling configuration item
 * option = { 
 * 		leading: false, // Prohibit first execution
 * 		trailing: false // Prohibit last execution
 *	}
 *		leading And tracking cannot be false at the same time [both false, throttling failure]
 * @param {Number} [duration=1000] Throttling duration
 */
function throttle (func, option, duration = 1000) {
    !option && (option = { leading: true, trailing: true });
    let timer, prev = 0;
    
    let trottled = function (...args) {
        const now = Date.now();
        if (!(option.leading || prev)) {
            prev = now;
        }
        // Solve the first non execution
        if (now - prev > duration) {
            // When the execution of a timer ends, clear the timer
            if (timer){
                clearTimeout(timer);
                // If the timer exceeds the duration and still does not execute the function [affected by other things], cancel the timer and execute the function immediately
           		timer = null;
            }
            func.apply(this, args);
            prev = Date.now();
        } else if (!timer && option.trailing) { // Solve the last non execution
            // Used when the duration of two calls is less than duration
            timer = setTimeout(() => {
                clearTimeout(timer);
                prev = Date.now();
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    };
    
    trottled.cancel = function () {
        clearTimeout(timer);
        timer = null;
        prev = 0;
    }
    return trottled;
}

window.requestAnimationFrame throttling

The throttling timing is not controllable [the function is called when it is unknown, because it is unknown when the browser will redraw]

/**
 * Function throttling
 * @param {Function} func Functions requiring throttling
 */
function throttle(func) {
    let lock = false;
    return function (...args) {
        !lock && window.requestAnimationFrame(() => {
            func.apply(this, args);
            lock = false;
        });
        lock = true;
    }
}

reference resources

JS series event throttling (Debounce)

[JavaScript] throttle - debounce does not point north

Anti shake throttling of JS handwriting series

JS event throttle

Use requestAnimationFrame instead of throttle to optimize page performance

Keywords: ElasticSearch

Added by smti on Mon, 03 Jan 2022 21:22:26 +0200