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
Use requestAnimationFrame instead of throttle to optimize page performance