preface

Throttling and debounce is one of the best ways to optimize web performance, which is often overlooked in the development process. Interview questions are also frequently asked, and are important for the front end. This article from the overview, implementation and source code of three parts, from shallow to deep to analyze and explain the throttling and anti-shake principle and implementation, so that readers can understand the principle and be able to hand out the relevant code.

An overview of the

Before explaining debounce and throttle, let’s look at the following example

Here is my modified Baidu, and we see that as soon as we enter, the console synchronizes the output. One detail is that when the keyDown trigger is used initially, the value in the input is null, meaning that no information has been entered

The core code

let search = document.getElementById("kw");
search.addEventListener('keydown'.function(){
     console.log(node.value)
})
Copy the code

Once a user enters a keydown, Baidu queries the relevant term based on the request. If we continuously input, first reduce the performance of the front end, input too fast or too slow network speed will appear delayed request stuck, increase the pressure on the back-end server. Now use the underscore function to see the effect of adding the underscore

Anti – shake – make the input box more intelligent, the user input over a certain period of time before output results

The core code

window.onload = function(){
    function print(){
        console.log(node.value);
    }
    var _print = _.debounce(print, 600)
    let node = document.getElementById("kw");
    node.addEventListener('keydown',_print);
}
Copy the code

Use the underscore tool function debounce directly. The first argument is the content you want to trigger and the second argument is explained as follows:

postpone its execution until after wait milliseconds have elapsed since the last time

That is to say,

Delay last execution time wait milliseconds. In this case, it means that you are typing continuously on the keyboard. If the interval between two inputs is more than 600ms, execute the function print.

Stabilization is used to delay the execution of the final action. The purpose of throttling is the same as that of shaking prevention, but underscores are slightly different

when invoked repeatedly, will only actually call the original function at most once per every waitmilliseconds

Which translates to

When repeated calls are made, only the original function is actually fired, and the wait time for that function is at most wait milliseconds.

What do you mean? Var _print = _. Throttle (print, 1000); var _print = _. Throttle (print, 1000); var _print = _.


Throttling – In continuous input, we see that the throttling is very regular, printing every 1s.

The core code

window.onload = function(){
    function print(){
        console.log(node.value)
	}
	var _print = _.throttle(print, 1000)
	let node = document.getElementById("kw");
	node.addEventListener('keydown',_print);
}
Copy the code

Let’s take another example 🌰. If we compare Baidu’s acceptance of user requests to the problem of taking passengers on the platform, once the passengers arrive on the platform, they get on the taxi and drive away before the government has no management (no anti-shaking and throttles). When there are too many people, there will be too many cars, and the traffic is crowded (the server pressure becomes bigger). At this time, the government says that I will intervene in the management (throttling and anti-shaking). The government stipulates that passengers should be received by bus, and has formulated two rules:

  1. The bus stops at the platform. When the first passenger gets on, the conductor starts counting. Thirty minutes later, the train leaves
  2. The bus stops on the platform, and the conductor counts every time a passenger gets on the train. If it takes more than 5 minutes, the conductor thinks that there is no one to follow and this should be the last passenger. The train leaves

Rule 1 is throttling (the first person in charge) and Rule 2 is shaking (the last person in charge). Both methods can reduce traffic pressure. Scroll event, resize event, mouse event (such as Mousemove, Mouseover, etc.), keyboard event (keyup, KeyDown, etc.) exist in the callback time frequently triggered. Using Throttle and Debounce can improve front-end performance and reduce server stress.

implementation

In fact, the concept of throttling and anti – shake, role and difference, the following we according to the principle of code implementation

The throttle

Cutting back, as outlined, is “first man in charge.” In the above example, when the keyboard is pressed in the Baidu search box for the first time, it starts timing and waits for “a certain period of time” to execute, and the triggering event within this period of time is directly shielded by the “throttle valve”. A throttling function can be written roughly based on this idea.

  // fn is the event callback we need to wrap, and interval is the threshold of the time interval
  Function.prototype.throttle = (fn, interval) = >{
    // last indicates the time when the callback was last triggered
    let last = 0;

    // return the throttle result as a function
    return function () {
        // Preserve the this context when calling
        let context = this
        // Retain the arguments passed in when the call is made
        let args = arguments
        // Record the time when the callback is triggered
        let now = +new Date(a)// Check whether the difference between the time triggered last time and the time triggered this time is smaller than the threshold of the interval
        if (now - last >= interval) {
        // If the interval is greater than the interval threshold we set, the callback is executedlast = now; fn.apply(context, args); }}}Copy the code

The throttling function inputs a function and returns a function (higher-order function). Throttling uses closures to save the last time when the callback was triggered (last), execute functions (fn), and time thresholds (interval). When fn is to be executed, the current time is compared with the last time when the callback was triggered. If the interval is greater than interval(now-last >= interval), Apply (context, args).

Image stabilization

Anti-shake is “the last word”, also use the above example, when in the search box every time press the keyboard, start a “timer”, if in the specified time again press, clear the previous timer, and then create a new one. The timer is used to trigger the FN when the delay time expires. So the code we implement is as follows:

// fn is the event callback we need to wrap, and delay is the wait time for each delayed execution
function debounce(fn, delay) {
  / / timer
  let timer = null
  
  // Return the debounce result as a function
  return function () {
    // Preserve the this context when calling
    let context = this
    // Retain the arguments passed in when the call is made
    let args = arguments

    // Each time an event is triggered, the previous timer is cleared
    if(timer) {
        clearTimeout(timer)
    }
    // Set a new timer
    timer = setTimeout(function () {
      fn.apply(context, args)
    }, delay)
  }
}

Copy the code

The stabilization function is also a higher-order function that also uses a closure, which, unlike throttling, holds the timer returned by setTimeout, which is used to cancel the timer before it is fired continuously.

The underscore the source code

Understand the concept and basic implementation of anti-shake and throttling (this part needs to explain the principle, handwritten implementation). Let’s take a look at the implementation of underscore for throttling and stabilization

The throttle

Understanding the implementation of throttling and stabilization above makes it a lot easier to look at the source code for underscore. The code implementation is posted below, and I have commented on it

  _.throttle = function(func, wait, options) {
    //timeout stores timer context stores context args stores parameters of func Result Stores results of func execution
    var timeout, context, args, result;
    var previous = 0;// Record the last time func was executed. Default: 0, the first func must be executed (now-0) greater than wait
    if(! options) options = {};/ / the default options

    // The timer function
    var later = function() {
      // Record the execution time of this function
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);// Execute the function func
      if(! timeout) context = args =null;
    };

    var throttled = function() {
      var now = _.now();// The current time
      // If not executed the first time, previous equals the current time
      if(! previous && options.leading ===false) previous = now;
      // Time interval -(current time - last execution time)
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // If remaining<0, it is longer than wait, if (now-previous)<0, which is now
      if (remaining <= 0 || remaining > wait) {
        // Clear the timer
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;// Record the current execution time
        result = func.apply(context, args);// Execute the function func
        if(! timeout) context = args =null;
      } else if(! timeout && options.trailing ! = =false) {
        // Execute func on time if trailing is true
        timeout = setTimeout(later, remaining);
      }
      return result;
    };

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

    return throttled;
  };
Copy the code

The underscore function uses options, and options have two configuration items, leading and trailing, because the function executes the func(previous=0) as soon as possible at the first time of the throttling function default. If you want to disable the first execution, the first execution of the function is disabled. Pass {leading:false}, or {trailing: false} if you want to disable the last execution. Underscore uses if (! Previous && options.leading === false) previous = now, leading === false) previous = now, leading === false) previous = now, leading === false) previous = now Underscore uses timers to control whether or not the last time the underscore needs to be executed, if (! timeout && options.trailing ! ==false) indicates that if trailing is set to false the timer will not be triggered and will not be executed. The default is to execute the last time because option.trailing=undefined, undefined! ==false is true, so the timer can be executed.

Image stabilization

  _.debounce = function(func, wait, immediate) {
    //timeout Return value of the storage timer result Return func result
    var timeout, result;
		
    // The timer triggers the function
    var later = function(context, args) {
      timeout = null;
      if (args) result = func.apply(context, args);
    };

    var debounced = restArguments(function(args) {
      if (timeout) clearTimeout(timeout);// If there is a timer, clear the original timer first
      if (immediate) {
        varcallNow = ! timeout; timeout = setTimeout(later, wait);// Start a timer
        if (callNow) result = func.apply(this, args);// If immediate is true, the function is executed immediately
      } else {
        timeout = _.delay(later, wait, this, args);// Start a timer as well
      }

      return result;
    });

    debounced.cancel = function() {
      clearTimeout(timeout);
      timeout = null;
    };

    return debounced;
  };
Copy the code

The debouce function underscore has the immediate argument, and when the immediate is true, debounce calls the function at the beginning of the wait interval.

conclusion

Throttling and stabilization is a very important knowledge of JavaScript, we first need to know the throttle is “the first word,” the follow-up will be throttle valve block, image stabilization is “the last word,” the evil devil each would start a ticking time bomb, only the back of the time bomb to pull down the front of the bomb, But it still ends up delaying the detonation. According to this idea, we can implement them by hand using the idea of closures. The underscore source code allows us to make better and more flexible use of them. Finally, I posted an address made by dao Friends to help us intuitively understand throttling and anti-shaking. In addition, underscore source code series has been collated into language sparrow, click here

Reference documentation

Function Stabilization and Function Throttling Underscore Chinese Document Front-end Performance Optimization Principles and Practices