Update: Thank you for your support, recently toss a summary of information, convenient for you to read the system, there will be more content and more optimization, click here to view

—— The following is the text ——

The introduction

In the last section we talked about the definition of higher-order functions and three types of applications of currying, and finally implemented a general function called currying. This section continues our discussion of function throttles and provides some definitions, implementation principles, and implementations of these higher-order functions into underscore.

Feel free to leave your thoughts or comments in the comments section. Below is a mind map of this article. See Github for hd mind maps and more articles.

Definition and Interpretation

Function throttling refers to the fact that a function is executed only once in an interval of 3 seconds, during which subsequent function call requests are ignored and the interval is not extended. Execution is triggered when a new function call is encountered for the first time after the 3-second interval, and subsequent function call requests are ignored for the next 3 seconds, and so on.

Take a small example, I don’t know if you have raised a small goldfish when you were a child, goldfish must be connected with water, at the beginning of the water pipe water flow is very large, water to half full began to tighten the faucet, reduce the speed of water into a drop of 3 seconds, through the drop of water to the small goldfish to increase oxygen.

At this point, “water in the pipe” is the callback task that comes in constantly due to our frequent manipulation of events, and it needs to accept the “faucet” arrangement; “Faucet” is the throttle valve, control the flow of water, filter ineffective callback tasks; A “drip” is a function executed every once in a while, and “3 seconds” is the interval, which is the basis of the “tap” decision to “drip”.

If you’re still confused, the image below makes it a lot clearer, and click on this page to see a visual comparison of throttling and anti-shock. Regular is the case where no processing is done, throttle is the result of throttling the function, and debounce is the result of buffeting the function (described in the next section).

Principle and Implementation

Function throttling is ideal for scenarios where functions are frequently called, such as window.onresize() events, mousemove events, upload progress, etc. Using the Throttle API is easy, but how do you implement the function Throttle?

There are two implementation schemes

  • Have the first one is judged by timestamp to the execution time, record the last execution time stamp, and then every time the triggering event callback, the callback is to judge the current timestamp from the last execution time stamp is the interval of reaching time difference (Xms), if is executed, the last time and update the timestamp of execution, so cycle.

  • The second method is to use timers. For example, when the Scroll event is triggered, print a Hello World, and then set a timer for 1000ms. After that, every scroll event is triggered to trigger a callback. The handler is cleared and the timer is reset.

In this case, we use the first approach, which saves a previous variable through the closure and determines the time difference between the current time and the previous when the throttle function is triggered each time. If the time difference is less than the wait time, the event triggering is ignored. If the wait time is greater than, set previous to the current time and execute fn.

Let’s implement this step by step, first implementing a closure to hold the previous variable.

const throttle = (fn, wait) = > {
	// The last time this function was executed
  let previous = 0
  return function(. args) {
    console.log(previous) ... }}Copy the code

Executing throttle returns a new function, which we’ll call betterFn.

const betterFn = function(. args) {
  console.log(previous)
    ...
}
Copy the code

The previous variable is available in the betterFn function and can be modified. BetterFn () is executed when a callback listener or event is triggered, so the difference between the current and previous is determined in this new function.

const betterFn = function(. args) {
  let now = +new Date(a);if (now - previous > wait) {
    previous = now
    // Execute the fn function
    fn.apply(this, args)
  }
}
Copy the code

The combination of the above two pieces of code implements the throttling function, so the complete implementation is as follows.

// fn is the function that needs to be executed
// Wait is an interval of time
const throttle = (fn, wait = 50) = > {
  // The last time fn was executed
  let previous = 0
  // return the throttle result as a function
  return function(. args) {
    // Get the current time and convert it to a timestamp in milliseconds
    let now = +new Date(a)// Compare the current time to the last time the function was executed
    // Set previous to the current time and execute fn if the wait time is greater than that
    if (now - previous > wait) {
      previous = now
      fn.apply(this, args)
    }
  }
}

// DEMO
// Execute throttle to return the new function
const betterFn = throttle(() = > console.log('Fn function executed'), 1000)
BetterFn is executed every 10 milliseconds, but only if the time difference is greater than 1000
setInterval(betterFn, 10)
Copy the code

Underscore

The above code implements a simple throttling function, but underscore implements a more advanced function that adds two new functions

  • Configure whether to respond to the initial callback of the event (leading parameter, ignored if false)
  • Configure whether to respond to the callback after the end of the event (trailing parameter, ignored if false)

When {leading: false} is configured, the initial callback is not executed. When {trailing: false} is configured, the trailing callback is not executed, but it is important to note that both cannot be configured at the same time.

So there are three calls to the throttling functions in underscore, the default (trailing), the {leading: false}, and the {trailing: false}. As mentioned above, there are two ways to implement throttle, one is to determine by timestamp, and the other is to control by timer creation and destruction.

The problem with the first solution is that {trailing: true} does not work because the event does not respond to a callback when it stops firing.

There is also a problem with the second approach, because the timer is executed late, so the trailing: false} does not work.

Underscore takes a scenario where two approaches are used together to achieve this functionality.

const throttle = function(func, wait, options) {
  var timeout, context, args, result;
  
  // The last time the callback was executed
  var previous = 0;
  
  // If no arguments are passed in, initialize options as an empty object
  if(! options) options = {};var later = function() {
    // set {leading: false}
    // Set previous to 0 each time the callback is triggered
    // Otherwise, the current time
    previous = options.leading === false ? 0 : _.now();
    
    // To prevent memory leaks, set it to null for later reference! Timeout Sets a new timeout
    timeout = null;
    
    // Execute the function
    result = func.apply(context, args);
    if(! timeout) context = args =null;
  };

  // This function is executed each time the event callback is triggered
  // check whether func is executed within the function
  // func is the function our business layer code wants to execute
  var throttled = function() {
    
    // Record the current time
    var now = _.now();
    
    // The first time the previous timestamp is 0
    // set {leading: false} (leading: false)
    // Set previous to the current value
    if(! previous && options.leading ===false) previous = now;
    
    // The time to wait until the next func trigger
    var remaining = wait - (now - previous);
    context = this;
    args = arguments;
    
    // Remaining <= 0 or remaining <= 0
    {leading: false} is not passed, and the first callback is triggered immediately
    // Wait - (now - previous) <= 0
    // The previous value is then quickly set to now
    if (remaining <= 0 || remaining > wait) {
      if (timeout) {
        clearTimeout(timeout);
        
        // clearTimeout(timeout) does not set timeout to null
        // Set it manually for subsequent judgment
        timeout = null;
      }
      
      // Sets previous to the current time
      previous = now;
      
      // Execute the func function
      result = func.apply(context, args);
      if(! timeout) context = args =null;
    } else if(! timeout && options.trailing ! = =false) {
      // The last time you need to trigger
      // If a timer already exists, the if branch will not be entered
      // If {trailing: false} is the last time that it does not need to trigger, it will not enter the branch
      // The later method is triggered after remaining milliseconds
      timeout = setTimeout(later, remaining);
    }
    return result;
  };

  // Manually cancel
  throttled.cancel = function() {
    clearTimeout(timeout);
    previous = 0;
    timeout = context = args = null;
  };

  // Execute _. Throttle to return the throttled function
  return throttled;
};
Copy the code

summary

  • Function throttling refers to the fact that a function is executed only once in a certain interval of time (for example, 3 seconds), during which subsequent function call requests are ignored

  • Throttling can be understood as tightening the faucet to drain water when raising goldfish, one drop in three seconds

    • The “water in the pipeline” is the callback task that comes in because we frequently manipulate events, and it needs to accept the “faucet” arrangement
    • The “faucet” is the throttle valve that controls the flow of water and filters out ineffective callback tasks
    • A drip is a function executed every once in a while
    • “3 seconds” is the interval time, it is the “faucet” decision “drip” basis
  • There are two throttling implementation schemes

    • Have the first one is judged by timestamp to the execution time, record the last execution time stamp, and then every time the triggering event callback, the callback is to judge the current timestamp from the last execution time stamp is the interval of reaching time difference (Xms), if is executed, the last time and update the timestamp of execution, so cycle.

    • The second method is to use timers. For example, when the Scroll event is triggered, print a Hello World, and then set a timer for 1000ms. After that, every scroll event is triggered to trigger a callback. The handler is cleared and the timer is reset.

reference

underscore.js

Front-end performance optimization principles and practices

The implementation of the restriction for the underscore function

Article shuttle

  • Further currification of applications of higher-order functions
  • A Brief analysis of JavaScript higher-order functions
  • [Advanced 5-3] Further explore the Function & Object egg problem
  • Step 5-2: Diagram prototype chains and their inheritance advantages and disadvantages
  • Reconceptualize constructors, stereotypes, and stereotype chains