A Brief Talk on Throttle and Shake Prevention

Keywords: Javascript Windows less github

Throttling and anti-shaking are common in the process of developing projects, such as input input real-time search, scroll lview scroll update, and so on. A large number of scenarios need to be processed. Let's introduce it by Lodash and go straight to the topic.

Lodash

API

  • debounce: multiple triggers, only the last trigger, the execution of the objective function.

    lodash.debounce(func, [wait=0], [options={}])
  • throttle: Limit the frequency of target function calls, such as: 2 calls in 1s.

    lodash.throttle(func, [wait=0], [options={}])

lodash defines a number of options in the opitons parameter, mainly the following three:

  • leading: The function is called at the beginning of each wait delay, with the default value false
  • trailing: The function is called at the end of each wait delay, with the default value true
  • maxwait: The maximum waiting time, because if debounce's function call time does not meet the conditions, it may never be triggered, so this configuration is added to ensure that a function can be executed once more than a period of time.

According to the combination of learning and trailing, different invocation effects can be achieved:

  • {lead: true, trailing: false}: Called only at the beginning of the delay
  • {leading: false, trailing: true}: By default, the function will not be called until the end of the delay
  • {lead: true, trailing: true}: Called at the beginning of the delay and after the delay ends

deboucne also has a cancel method to cancel jitter-proof calls

Use

  • Deounce:

    addEntity = () => {
      console.log('--------------addEntity---------------')
      this.debounceFun();
    }
     
    debounceFun = lodash.debounce(function(e){
      console.log('--------------debounceFun---------------');
    }, 500,{
      leading: true,
      trailing: false,
    })

    When the first click is executed, click continuously and within 500 ms, no longer execute, click again after 500 ms, execute.

  • throttle:

    addEntity = () => {
      console.log('--------------addEntity---------------');
      this.throttleFun();
    }
     
    throttleFun = lodash.throttle(function(e){
      console.log('--------------throttleFun---------------');
    }, 500,{
      leading: true,
      trailing: false,
    })

    When the first click is executed, click continuously and within 500 ms, then automatically execute once after 500 ms. (Note: If the number of consecutive clicks is less than 500 ms, it will not execute automatically), then click again after 500 ms.

Source Code Implementation

debounce

// This is used to get the current timestamp
function now() {
  return +new Date()
}
/**
 * Anti-jitter function. When the return function is called continuously, the idle time must be greater than or equal to wait before func can execute.
 *
 * @param  {function} func        callback
 * @param  {number}   wait        Represents the interval between time windows
 * @param  {boolean}  immediate   Whether to call the function immediately when set to ture
 * @return {function}             Return the client call function
 */
function debounce (func, wait = 50, immediate = true) {
  let timer, context, args

  // Delayed execution function
  const later = () => setTimeout(() => {
    // Delay function execution completed, empty the cache timer serial number
    timer = null
    // In the case of delayed execution, the function executes in the delayed function
    // Parameters and contexts used previously cached
    if (!immediate) {
      func.apply(context, args)
      context = args = null
    }
  }, wait)

  // The function returned here is the function actually called each time.
  return function(...params) {
    // If no delayed execution function is created, a delayed execution function is created.
    if (!timer) {
      timer = later()
      // If it is executed immediately, call the function
      // Otherwise, cache parameters and call context
      if (immediate) {
        func.apply(this, params)
      } else {
        context = this
        args = params
      }
    // If there is a late execution function, clear the original and reset one when calling
    // Doing so will cause the delay function to recount
    } else {
      clearTimeout(timer)
      timer = later()
    }
  }
}

throttle

/**
 * underscore Throttle function. When the return function is called continuously, the execution frequency of func is limited to sub / wait.
 *
 * @param  {function}   func      callback
 * @param  {number}     wait      Represents the interval between time windows
 * @param  {object}     options   If you want to ignore the call to the start function, pass in {leading: false}.
 *                                If you want to ignore the call to the end function, pass in {trailing: false}
 *                                They can't coexist, otherwise the function can't be executed.
 * @return {function}             Return the client call function
 */
_.throttle = function(func, wait, options) {
    var context, args, result;
    var timeout = null;
    // Previous timestamps
    var previous = 0;
    // If options are not passed, set it to an empty object
    if (!options) options = {};
    // Timer callback function
    var later = function() {
      // If leading is set, set previous to 0
      // The first if judgment for the following functions
      previous = options.leading === false ? 0 : _.now();
      // Vacuum one is to prevent memory leaks, the other is for the following timer judgment
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      // Get the current timestamp
      var now = _.now();
      // The first entry must be true
      // If you need to execute the function for the first time
      // Set the last timestamp to the current one
      // This way, the value of remaining will be greater than 0 in the next calculation.
      if (!previous && options.leading === false) previous = now;
      // Calculate the remaining time
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // If the current call is longer than the last call time + wait
      // Or the user manually schedules the time
       // If trailing is set, only this condition will be entered.
      // If leading is not set, this condition will be entered for the first time.
      // Also, you may feel that if you turn on the timer, you should not enter the if condition.
      // In fact, it will still enter because of the delay of the timer.
      // It's not the exact time. You probably set it for 2 seconds.
      // But it takes 2.2 seconds for him to trigger, and that's when he enters the condition.
      if (remaining <= 0 || remaining > wait) {
        // Clean up if a timer exists or call a second callback
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // Determine whether timers and trailing are set
        // Turn on a timer if you don't have one.
        // And you can't set up leading and trailing at the same time.
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

In the end, welcome star: https://github.com/sisterAn/blog

Welcome attention: front-end bottle gentleman

Posted by TRemmie on Mon, 22 Jul 2019 19:23:17 -0700