JS throttling and anti – shake
Function throttling: effective for the first time within a certain interval
It specifies a time interval in which only a callback that triggers an event can be executed. If the event is triggered more than once within the time interval, no callback is executed. Application scenario: Can reduce the trigger frequency of some events, such as lazy loading to listen to calculate the position of the scroll bar, click lottery, buy
By definition, let’s write a throttling function according to the understanding of small white, Vue as an example:
- Suppose we use the throttling function that is wrapped around the button binding
clickMe
Method inside - Declare an event state variable
timer
Declare the anti-shake functionthrottle
- The stabilization function takes two arguments, one of which is passed the method that should be executed
cb
, one is the time interval for shaking preventiondelay
- Enter the method if
timer
Is true, thenreturn
Don’t follow the logic that follows - if
timer
为null
, starts a timer and assigns a value totimer
, the waiting time of the timer isdelay
Parameter, timer after execution, clear timer, and willtimer
Restore to the initial valuenull
- Finally, the passed method is executed
cb
- So the first time we click the button,
timer
为null
, execute timer, executecb
After,1s
If I click on it again,timer
If true, no subsequent logic is executed,1s
And then I click again,timer
为null
, the command can be executed again
<template>
<button @click="clickMe('Jerry', 'Tom')">PM me!</button>
</template>
<script>
/ / throttling
// declare an event state variable
let timer = null
// 3. Two parameters: one is the method of transmission, the other is the time interval of anti-shake
function throttle (cb, delay) {
// if timer is true, return
if (timer) return
Otherwise, start a timer and assign a value to timer. The waiting time of the timer is the delay parameter. After the timer is executed, clear the timer and restore the timer to the initial value null
timer = setTimeout(() = > {
clearTimeout(timer)
timer = null
}, delay)
// Execute the pass method cb
return cb()
}
export default {
methods: {
clickMe (name, name2) {
// 1. Suppose we use the throttling function wrapped in the clickMe method we bind to the button
throttle(() = > {
console.log('the point! ', name)
console.log('the point! ', name2)
}, 1000)}}}</script>
Copy the code
However, this implementation is problematic because timers are defined in the global scope, so they are improved in the form of closures:
- now
throttle
Bind directly toclickMe
On, - When the current component is initialized,
clickMe
The binding ofthrottle
Will be executed once,throttle
Will pass us oncb
Method for packaging, and thenreturn
A new way to giveclickMe
- At this time
timer
The scope of thethrottle
中
<template>
<button @click="clickMe('Jerry', 'Tom')">PM me!</button>
</template>
<script>
/ / throttling
function throttle (cb, delay) {
/ / timer
let timer
// When throttle is called, cb is wrapped, executed, and returned to the caller
return function () {
// If the timer exists, the user is prevented from operating
if (timer) return
// Otherwise start a timer
timer = setTimeout(() = > {
// The interval ends, the timer ends
clearTimeout(timer)
// Initialize the timer
timer = null
}, delay)
// Perform the callbackcb(... arguments) } }export default {
methods: {
// Throttle is now tied directly to clickMe, and throttle will return a new method
clickMe: throttle((name, name2) = > {
console.log('the point! ', name)
console.log('the point! ', name2)
console.log(this) // undefined
}, 1000)}}</script>
Copy the code
Here are a few points:
arguments
And residual parameters
- In ordinary functions
function
To obtain all parameter objects, use the keywordarguments
You can get - In the arrow function, no
arguments
, can pass the remaining parameters(... args)
To obtain
However, we can’t get this in the present method, so we are executing cb(… Arguments) need to modify this pointer inside cb, there are three methods: Modify this to point to
cb.call(this, ... arguments)
cb.apply(this, arguments)
cb.bind(this)(... arguments)
So we can get the “this” pointer correctly.
We can use date.now () instead of a timer to improve performance. Here is the complete code:
<template>
<button @click="clickMe('Jerry', 'Tom')">PM me!</button>
</template>
<script>
/ / throttling
function throttle (cb, delay) {
// The time of the last operation
let lastTime = 0
return function () {
const triggerTime = Date.now()
// Current operation time - Last operation time > Interval
if (triggerTime - lastTime > delay) {
cb.call(this. arguments) lastTime = triggerTime } } }export default {
methods: {
clickMe: throttle(function (name, name2) {
console.log('the point! ', name)
console.log('the point! ', name2)
console.log('the point! '.this)},1000)}}</script>
Copy the code
Two, function tremble: within a certain interval, the last effective
If the event is triggered again within this time interval, the timer is reset and the callback is not executed.
Application scenario: Search box (if the user does not enter the content again after n seconds, search), zoom in and out of the listening window resize, scroll listening
function debounce(cb, delay = 500) {
/ / timer
let timer = null
return function (. args) {
// Clear the timer if it exists and reset the timer (if the user keeps typing, reset the timer)
if (timer) clearTimeout(timer)
// Start timer (after delay time, the user does not enter, then start search)
timer = setTimeout(() = > {
// When the interval is reached, the callback is executed
cb.call(this, args)
}, delay)
}
}
Copy the code
Use: Use anti-shake in onScorll
// Wrap the scroll callback with debounce
const scroll = debounce(() = > {
console.log("Triggered a scroll event.")},1000)
document.addEventListener("scroll", scroll)
Copy the code
ThrottlePlus: Use Debounce to optimize throttle
The problem with Throttle is that it’s too patient, assuming:
- First we are going to
delay
Set to3s
Is executed when the user clicks it for the first timecb
- Users went crazy, but it didn’t happen
3s
The click stops before this time point is reached - So except for the user’s first click it’s going to execute
cb
In addition, none of the subsequent clicks will be executedcb
- As a result, users never get feedback on their actions and feel “stuck” on the page.
So, to solve this problem, we need to combine throttle and debounce, so that if the user finishes before 3s, cb is executed once the timer reaches 3s
function throttlePlus (cb, delay) {
// The time of the last operation
let lastTime = 0
/ / timer
let timer = null
return function () {
/** @ not in the interval **/
// The current operation time
const triggerTime = Date.now()
// If the time between the current operation and the time of the last operation is greater than the interval, go to cb
if (triggerTime - lastTime > delay) {
lastTime = triggerTime
cb.call(this. arguments) }else {
/** @ within the interval **/
// If the user keeps operating, the timer is reset
timer && clearTimeout(timer)
// Set the timer and execute it once when the delay interval is reached
timer = setTimeout(() = > {
lastTime = triggerTime
clearTimeout(timer)
cb.call(this. arguments) }, delay) } } }Copy the code
Use: Enhanced throttlePlus in onScorll
// Wrap scroll callback with throttlePlus
const scroll = throttlePlus(() = > {
console.log("Triggered a scroll event.")},1000)
document.addEventListener("scroll", scroll)
Copy the code