Throttling definition
Some frequently operated events will affect performance. "Throttling" is used to control the response interval. When the event is triggered, the corresponding function will not be triggered immediately, but will execute the response function according to a specific time interval every time the response interval is reached.
Throttling case
In the "aircraft war" in the online game, the keyboard keys can be used to launch bullets and tap the keyboard quickly and continuously. The aircraft will not launch continuously, but control the distance between bullets at a certain time interval. For example, if the system is set to fire bullets once a second, even if you hit the keyboard 20 times in a second, only one bullet will be sent.
Throttling usage scenario
In the process of programming, many scenarios can use "throttling".
- Frequent input and search in the input box
- Click the button frequently and submit information to trigger an event
- Listen for browser scrolling events
- Listen for browser zoom events
When throttling is not used
Here, a commodity search box is simulated. We need to call the interface for association query on the content entered by the user to give the user search tips.
When anti shake is not used, we will directly bind the function to the corresponding event.
// html <input /> // js code const inputEl = document.querySelector("input"); let count = 0; function inputEvent(event) { console.log(`${++count}Input times, the obtained content is:${event?.target?.value}`); } inputEl.oninput = inputEvent;
Enter "JavaScript csses6" in the input box, a total of 16 characters, so the method was called 16 times
The performance of this method is very low, because the interface is called every time a character is input, which causes great pressure on the server. The function of "throttling" executes the function according to the specified time, avoiding the waste of resources caused by multiple executions.
Self determined throttle function
The principle of throttling function implementation is to execute the function according to the specified time interval.
Step 1: basic version throttling implementation
Judge the time interval between the current and the last execution of the function. If the specified time interval is exceeded, the function will be executed.
function throttle(fn, interval) { // Set the initial time to 0 let startTime = 0; const _throttle = function () { // Get current time let currentTime = new Date().getTime(); // Gets the remaining time (the distance between the current time and the specified interval) let restTime = interval - (currentTime - startTime); // When the remaining time is less than or equal to 0, execute the function if (restTime <= 0) { // Execute the passed in function fn(); // Assign the current time to the initial time startTime = currentTime; } }; return _throttle; } inputEl.oninput = throttle(inputEvent, 2000);
The time interval specified here is 2 seconds, that is, the function is executed once in 2 seconds.
But at this time, we found that the parameter was not passed, and in fact, the direction of this is wrong
Step 2: expand this and parameters
Use the apply method to change the direction of this and pass parameters
function throttle(fn, interval) { // Set the initial time to 0 let startTime = 0; const _throttle = function (...args) { // Get current time let currentTime = new Date().getTime(); // Gets the remaining time (the distance between the current time and the specified interval) let restTime = interval - (currentTime - startTime); // When the remaining time is less than or equal to 0, execute the function if (restTime <= 0) { // Change the direction and transfer parameters of this through apply fn.apply(this, args); // Assign the current time to the initial time startTime = currentTime; } }; return _throttle; }
At this point, both this and parameters can be obtained~
So far, most usage scenarios of throttling have been realized, and the following functions will be more complex.
Step 3: the function executes immediately
In the above function definition, when the first character is input, the function will be executed with a high probability, because the time of inputting the first character minus the initialization time of 0 seconds is generally greater than the set time interval.
If you don't think it is necessary to execute the function when you enter the first character, you can customize parameters to control whether the function will be executed immediately.
The parameter leading controls the immediate execution of the function, which defaults to true.
function throttle(fn, interval, options = {}) { let startTime = 0; // The default value of leading is set to true const { leading = true } = options ; const _throttle = function (...args) { let currentTime = new Date().getTime(); // When immediate execution is not required, modify the startTime with the initial value of 0 to the current time if (!leading && !startTime) { startTime = currentTime; } let restTime = interval - (currentTime - startTime); if (restTime <= 0) { fn.apply(this, args); startTime = currentTime; } }; return _throttle; } // Pass in the leading parameter inputEl.oninput = throttle(inputEvent, 2000, { leading: false, });
This will wait for 2s before the first function call is executed
Step 4: the last execution of the function
"Throttling" is only related to the interval of the function, and has nothing to do with the completion of the last character input.
Therefore, if the time interval between the last character input and the last function call does not reach the specified time interval, the function will not be executed at this time. If you need to execute, you need to customize parameters to control function execution.
The last execution of the function is controlled by the parameter trailing, which is false by default. When the function needs to be executed at the last time, set the timer before the execution of each time interval. When the function is executed at the time interval, clear the timer. If the specified interval is not reached after the last character is entered, execute the contents of the timer.
function throttle(fn, interval, options = {}) { let startTime = 0; // Set a timer let timer = null; // The default value of leading is set to true, and the default value of trailing is set to false const { leading = true, trailing = false } = options; const _throttle = function (...args) { let currentTime = new Date().getTime(); // When immediate execution is not required, modify the startTime with the initial value of 0 to the current time if (!leading && !startTime) { startTime = currentTime; } let restTime = interval - (currentTime - startTime); if (restTime <= 0) { // When there is a timer, clear the timer if (timer) { clearTimeout(timer); timer = null; } fn.apply(this, args); startTime = currentTime; // When the execution is completed, the following timer code will not be executed to avoid repeated execution return; } // If the last execution is required if (trailing && !timer) { // Set timer timer = setTimeout(() => { timer = null; fn.apply(this, args); // When immediate execution is required, the start time is assigned as the current time; otherwise, it is assigned as 0 startTime = !leading ? 0 : new Date().getTime(); }, restTime); } }; return _throttle; } // Pass in the leading and trailing parameters inputEl.oninput = throttle(inputEvent, 2000, { leading: false, trailing: true, });
At this time, after entering the last character, wait for the time interval (restTime) set in the timer, and the function will execute again.
Step 5: cancel the function
There may be such a scenario. When the user clicks Cancel when searching, or closes the page, there is no need to send the request again.
We add a cancel button and click to terminate the operation.
// html <input /> <button>cancel</button> // javascript function throttle(fn, interval, options = {}) { let startTime = 0; // Set a timer let timer = null; // The default value of leading is set to true, and the default value of trailing is set to false const { leading = true, trailing = false } = options; const _throttle = function (...args) { let currentTime = new Date().getTime(); // When immediate execution is not required, modify the startTime with the initial value of 0 to the current time if (!leading && !startTime) { startTime = currentTime; } let restTime = interval - (currentTime - startTime); if (restTime <= 0) { // When there is a timer, clear the timer if (timer) { clearTimeout(timer); timer = null; } fn.apply(this, args); startTime = currentTime; // When the execution is completed, the following timer code will not be executed to avoid repeated execution return; } // If the last execution is required if (trailing && !timer) { // Set timer timer = setTimeout(() => { timer = null; fn.apply(this, args); // When immediate execution is required, the start time is assigned as the current time; otherwise, it is assigned as 0 startTime = !leading ? 0 : new Date().getTime(); }, restTime); } }; // Define a cancellation method on the function object _throttle.cancel = function () { // When there is a timer, it is cleared if (timer) { clearTimeout(timer); timer = null; // Reset start time startTime = 0; } }; return _throttle; } // Get dom element const inputEl = document.querySelector("input"); const cancelBtn = document.querySelector("button"); const _throttle = throttle(inputEvent, 2000, { leading: false, trailing: true, }); // Binding event inputEl.oninput = _throttle; cancelBtn.onclick = _throttle.cancel;
When you click Cancel, the contents of the timer will not be executed
Step 6: function return value
After the above "throttling" function is executed, there is no return value. If the return value is required, there are two forms.
Callback function
Get the return value by passing the callback function in the parameter.
function throttle(fn, interval, options = {}) { let startTime = 0; // Set a timer let timer = null; // The default value of leading is set to true, the default value of trailing is set to false, and the incoming callback function is used to receive the return value const { leading = true, trailing = false, callbackFn } = options; const _throttle = function (...args) { let currentTime = new Date().getTime(); // When immediate execution is not required, modify the startTime with the initial value of 0 to the current time if (!leading && !startTime) { startTime = currentTime; } let restTime = interval - (currentTime - startTime); if (restTime <= 0) { // When there is a timer, clear the timer if (timer) { clearTimeout(timer); timer = null; } // Gets the result of executing the function const result = fn.apply(this, args); // Execute the incoming callback function if (callbackFn) callbackFn(result); startTime = currentTime; // When the execution is completed, the following timer code will not be executed to avoid repeated execution return; } if (trailing && !timer) { timer = setTimeout(() => { timer = null; // Gets the result of executing the function const result = fn.apply(this, args); // Execute the incoming callback function if (callbackFn) callbackFn(result); // When immediate execution is required, the start time is assigned as the current time; otherwise, it is assigned as 0 startTime = !leading ? 0 : new Date().getTime(); }, restTime); } }; // Define a cancellation method on the function object _throttle.cancel = function () { if (timer) { // When there is a timer, it is cleared clearTimeout(timer); timer = null; // Reset start time startTime = 0; } }; return _throttle; } const inputEl = document.querySelector("input"); const cancelBtn = document.querySelector("button"); // The incoming callback function is used to receive the return value const _throttle = throttle(inputEvent, 2000, { leading: false, trailing: true, callbackFn: (value) => { console.log("Get return value", value); }, }); inputEl.oninput = _throttle; cancelBtn.onclick = _throttle.cancel;
Each time the response function is executed, the callback function is executed.
promise
Get the return value by returning promise
function throttle(fn, interval, options = {}) { let startTime = 0; // Set a timer let timer = null; // The default value of leading is set to true, and the default value of trailing is set to false const { leading = true, trailing = false } = options; const _throttle = function (...args) { // Return results through promise return new Promise((resolve, reject) => { let currentTime = new Date().getTime(); // When immediate execution is not required, modify the startTime with the initial value of 0 to the current time if (!leading && !startTime) { startTime = currentTime; } let restTime = interval - (currentTime - startTime); if (restTime <= 0) { // When there is a timer, clear the timer if (timer) { clearTimeout(timer); timer = null; } // Gets the result of executing the function const result = fn.apply(this, args); // A successful response is returned through resolve resolve(result); startTime = currentTime; return; } if (trailing && !timer) { timer = setTimeout(() => { timer = null; // Gets the result of executing the function const result = fn.apply(this, args); // A successful response is returned through resolve resolve(result); // When immediate execution is required, the start time is assigned as the current time; otherwise, it is assigned as 0 startTime = !leading ? 0 : new Date().getTime(); }, restTime); } }); }; // Define a cancellation method on the function object _throttle.cancel = function () { if (timer) { // When there is a timer, it is cleared clearTimeout(timer); timer = null; // Reset start time startTime = 0; } }; return _throttle; } // Get dom element const inputEl = document.querySelector("input"); const cancelBtn = document.querySelector("button"); const _throttle = throttle(inputEvent, 2000, { leading: false, trailing: true, }); // apply is used to point this to the input element const promiseCallback = function (...args) { _throttle.apply(inputEl, args).then((res) => { console.log("promise Callback", res); }); }; // Binding event inputEl.oninput = promiseCallback; cancelBtn.onclick = _throttle.cancel;
promise calls the then method to get the return value
In development, the throttling function is used to optimize the performance of the project. It can be customized as above, or a third-party library can be used.
For the anti shake function, please refer to this article, Five steps of user-defined anti shake function to meet complex requirements
The above is the related content of anti shake function. There are still many places that developers need to master about js advanced. You can see other blog posts I wrote and keep updating~