Image stabilization

What is a debounce?

  • When an event is triggered, the corresponding function is not fired immediately, but is delayed.
  • When events fire in a row, the firing of the function is delayed forever.
  • The response function is actually executed only if the event is not triggered again after waiting some time.
  • To put it simply, stabilization is to delay the execution of a function for a certain amount of time. If the event is refired within that time, the delay time is reset, and the callback function is not executed until the delay time is actually reached.
  • For example: the return to the city in the game can be considered as anti-shaking, in the process of returning to the city, if the operation is performed again, then the second will be read again, only the whole process of reading is not performed again, then wait until the end of the successful return to the city.

Application scenarios of anti-shake

  • Input fields frequently enter content, search or submit information.
  • Clicking a button frequently triggers an event.
  • Listen for browser scroll events.
  • Listen for the user to resize the browser.

The case of anti-shake function

  • Listening for the onInput event of the search box, the callback function is executed once for each character entered. If we make network requests in the callback function, the user will send many network requests for the input content, which is very wasteful.

  • No effect using the anti – shake function

  • The effect of using the anti – shake function

  • As you can see, the anti-shake function is used. When the same event is triggered frequently, only the last time will be executed.

Realize the anti – shake function

Simple version of the anti – shake function

// The first argument is the function that needs to be buffered. The second argument is the delay time, which defaults to 1 second
function debounce(fn, delay = 1000) {
// The core of the anti-shake function is to use setTimeout
    // The time variable is used to hold the Id returned by setTimeout
    
    let time = null
    
    function _debounce() {
        // If time is not 0, that is, there is a timer, clear the timer
        if(time ! = =null) {
            clearTimeout(time)
        }
        
        time = setTimeout(() = > {
            fn()
        }, delay)
    }
    
    // The stabilization function returns another function that is actually called
    return _debounce
}
Copy the code
  • The realization of the above anti-shake function can basically achieve the effect of anti-shake, but there are still some small problems, such as the direction of this is inconsistent with the original function and parameter problems.

Anti – shake function implementation -this and parameter issues

// The first argument is the function that needs to be buffered. The second argument is the delay time, which defaults to 1 second
function debounce(fn, delay = 1000) {
// The core of the anti-shake function is to use setTimeout
    // The time variable is used to hold the Id returned by setTimeout
    
    let time = null
    
    // Save the parameters received by the callback into the args array
    function _debounce(. args) {
        // If time is not 0, that is, there is a timer, clear the timer
        if(time ! = =null) {
            clearTimeout(time)
        }
        
        time = setTimeout(() = > {
            // Use apply to change fn's this, passing the argument to fn
            fn.apply(this, args)  
        }, delay)
    }
    
    // The stabilization function returns another function that is actually called
    return _debounce
}
Copy the code
  • The current anti-shock function we implemented is basically fine, but if we want the event to fire for the first time, the function will execute immediately, and then it will be the same as before. The effect is shown below

  • To do this we should ask the user to pass in an argument that determines whether the first trigger should be executed.

The realization of the anti – shake function – the first time immediately triggered

// The first argument is the function that needs to be buffered. The second argument is the delay time, which defaults to 1 second
// Immediate determines whether to execute the first time immediately. The default value is false
function debounce(fn, delay = 1000, immediate = false) {
// The core of the anti-shake function is to use setTimeout
    // The time variable is used to hold the Id returned by setTimeout
    let time = null
    // The isImmediateInvoke variable is used to record whether or not to execute immediately. The default value is false
    let isImmediateInvoke = false
    
    // Save the parameters received by the callback into the args array
    function _debounce(. args) {
        // If time is not 0, that is, there is a timer, clear the timer
        if(time ! = =null) {
            clearTimeout(time)
        }
        
        // When the event is triggered for the first time, the first event needs to be triggered
        if(! isImmediateInvoke && immediate) { fn.apply(this, args)
            // Set the isImmediateInvoke to true so that subsequent function calls that are frequently triggered will not be affected
            isImmediateInvoke = true;
        }
        
        time = setTimeout(() = > {
            // Use apply to change fn's this, passing the argument to fn
            fn.apply(this, args)  
            // When the timer function is executed, it is the last event that frequently fires the event
            // Set the isImmediateInvoke to false so that the next first triggered event can be immediately executed
            isImmediateInvoke = false
        }, delay)
    }
    
    // The stabilization function returns another function that is actually called
    return _debounce
}
Copy the code
  • If we need to get the return value of the original function, our implementation above won’t be able to do that. Although it’s rare to need to get a return value, here’s how to do it.
  • If we want to get the return value, we can have the user pass a callback function, which can pass the return value as an argument to the callback function after the function is executed.
  • You can also pass the parameter by returning a Promise.
  • The method of callback function is used here

Realization of anti – shake function – return value problem

// The first argument is the function that needs to be buffered. The second argument is the delay time, which defaults to 1 second
// Immediate determines whether to execute the first time immediately. The default value is false
function debounce(fn, delay = 1000, immediate = false, resultCb) {
// The core of the anti-shake function is to use setTimeout
    // The time variable is used to hold the Id returned by setTimeout
    let time = null
    // The isImmediateInvoke variable is used to record whether or not to execute immediately. The default value is false
    let isImmediateInvoke = false
    
    // Save the parameters received by the callback into the args array
    function _debounce(. args) {
        // If time is not 0, that is, there is a timer, clear the timer
        if(time ! = =null) {
            clearTimeout(time)
        }
        
        // When the event is triggered for the first time, the first event needs to be triggered
        if(! isImmediateInvoke && immediate) {// Save the return value of the function to result
            const result = fn.apply(this, args)
            if (typeof resultCb === 'function') {
                // When the user passes a resultCb function, the function is executed and the result is passed as an argument.
                resultCb(result)
            }
            // Set the isImmediateInvoke to true so that subsequent function calls that are frequently triggered will not be affected
            isImmediateInvoke = true;
        }
        
        time = setTimeout(() = > {
            // Use apply to change fn's this, passing the argument to fn
            fn.apply(this, args)  
            // When the timer function is executed, it is the last event that frequently fires the event
            // Set the isImmediateInvoke to false so that the next first triggered event can be immediately executed
            isImmediateInvoke = false
        }, delay)
    }
    
    // The stabilization function returns another function that is actually called
    return _debounce
}
Copy the code

The throttle

What is throttling

  • Throttling is a response function that executes when an event is triggered.
  • But if the event is triggered frequently, the throttling function will execute the function at a certain frequency.
  • Throttling is similar to the skill CD, no matter how many times you press it, you must wait until the CD is finished before releasing the skill. That means that no matter how many times you fire an event during CD time, it only gets executed once. It will only be executed again the next time the CD is converted.

Use a rendering of the throttling function

Implement throttling function

// interval Specifies the length of the CD
function throttle(fn, interval) {
    // This variable is used to record the last function execution event
    let lastTime = 0
    
    const _throttle = function(. args) {
        // Get the current time
        const nowTime = new Date().getTime()
        
        // CD remaining time
        const remainTime = nowTime - lastTime
        // If the remaining time is greater than the interval, the function can be executed again
        if (remainTime - interval >= 0) {
            fn.apply(this, args)
            // Set the time of the last function execution to nowTime so that the CD can be re-entered next time
            lastTime = nowTime
        }
    }
    // return _throttle
    return _throttle
}
Copy the code
  • The above code is a basic throttling function implementation, but you’ll find that the first event response function executes immediately. This is because our lastTime is initially set to 0, and when we fire the event, the nowTime we get is a very large value, so subtracting lastTime will be greater than interval, so the first time will be executed immediately.

Implements a throttling function that controls whether the first function executes immediately

// The leading parameter controls whether to execute immediately for the first time. The default value is true
function throttle(fn, interval, leading = true) {
    // This variable is used to record the last function execution event
    let lastTime = 0
    // The internal control is executed immediately
    let isLeading = true
    
    const _throttle = function(. args) {
        // Get the current time
        const nowTime = new Date().getTime()
        
        // The first time does not need to be executed immediately
        if(! leading && isLeading) {// Set lastTime to nowTime so that the first time remainTime is not greater than the interval
            lastTime = nowTime
            // Set isLeading to false so that it does not affect subsequent lastTime.
            isLeading = false
        }
        
        // CD remaining time
        const remainTime = nowTime - lastTime
        // If the remaining time is greater than the interval, the function can be executed again
        if (remainTime - interval >= 0) {
            fn.apply(this, args)
            // Set the time of the last function execution to nowTime so that the CD can be re-entered next time
            lastTime = nowTime
        }
    }
    // return _throttle
    return _throttle
}
Copy the code
  • Implementation effect

  • You can see that the first function does not execute immediately, but when we do the next click, the first event of the next round will execute immediately again.
  • This is because when the next round of events is triggered, isLeading is already set to false, so lastTime will not be set to nowTime.
  • To solve this problem, change isLeading to true at the end of the event cycle, so that when the next event cycle starts, isLeading will be true.
  • When the remaining time is less than the interval, set a timer. The delay time of the timer is interval. Within the timer, set isLeading to true.
// The leading parameter controls whether to execute immediately for the first time. The default value is true
function throttle(fn, interval, leading = true) {
    // This variable is used to record the last function execution event
    let lastTime = 0
    // The internal control is executed immediately
    let isLeading = true
    // time Indicates the ID of the timer
    let time = null
    
    const _throttle = function(. args) {
        // Get the current time
        const nowTime = new Date().getTime()
        
        // The first time does not need to be executed immediately
        if(! leading && isLeading) {// Set lastTime to nowTime so that the first time remainTime is not greater than the interval
            lastTime = nowTime
            // Set isLeading to false so that it does not affect subsequent lastTime.
            isLeading = false
        }
        
        // CD remaining time
        const remainTime = nowTime - lastTime
        // If the remaining time is greater than the interval, the function can be executed again
        if (remainTime - interval >= 0) {
            fn.apply(this, args)
            // Set the time of the last function execution to nowTime so that the CD can be re-entered next time
            lastTime = nowTime
        }
        
        if (remainTime < interval) {
            // Check whether the timer exists. If so, cancel it
            if (time) clearTimeout(time)
            
            // Set the timer
            time = setTimeout(() = >{
                // Because of this timer, the interval will be executed after no event is triggered, that is, one round of events
                // The first event of the next round of events will not be executed immediately.
                isLeading = true
            }, interval)
        }
    }
    // return _throttle
    return _throttle
}
Copy the code
  • The throttling function implemented above does enough to control whether the first event is executed immediately, but it’s not enough. If we want the last event binding function in a round of click events to be able to execute, the throttling function we currently implement will not be able to do so.
  • In fact, in the code that has been implemented, it is very simple to achieve the last event binding function to execute, we just need to let the user pass a parameter, used to control whether to execute the last time, and then make a judgment in the timer can be realized.
The trailing parameter is used to control whether the last execution is performed. The default is false
function throttle(fn, interval, leading = true, trailing = false) {
    // This variable is used to record the last function execution event
    let lastTime = 0
    // The internal control is executed immediately
    let isLeading = true
    // time Indicates the ID of the timer
    let time = null
    
    const _throttle = function(. args) {
        // Get the current time
        const nowTime = new Date().getTime()
        
        // The first time does not need to be executed immediately
        if(! leading && isLeading) {// Set lastTime to nowTime so that the first time remainTime is not greater than the interval
            lastTime = nowTime
            // Set isLeading to false so that it does not affect subsequent lastTime.
            isLeading = false
        }
        
        // CD remaining time
        const remainTime = nowTime - lastTime
        // If the remaining time is greater than the interval, the function can be executed again
        if (remainTime - interval >= 0) {
            fn.apply(this, args)
            // Set the time of the last function execution to nowTime so that the CD can be re-entered next time
            lastTime = nowTime
        }
        
        if (remainTime < interval) {
            // Check whether the timer exists. If so, cancel it
            if (time) clearTimeout(time)
            
            // Set the timer
            time = setTimeout(() = >{
                // Determine whether the last execution is required
                if (trailing) {
                // It needs to be executed last time
                    fn.apply(this, args)
                }
                // Because of this timer, the interval will be executed after no event is triggered, that is, one round of events
                // The first event of the next round of events will not be executed immediately.
                isLeading = true
            }, interval)
        }
    }
    // return _throttle
    return _throttle
}
Copy the code

  • After constant modification, the current implementation of the throttle function, has been relatively perfect, if you want to obtain the return value, you can refer to the implementation of the anti-shake function.

Other handwriting functions and code

Js handwriting – common array methods

Js handwritten problem code