Anti – shake and throttling functions

The purpose of both anti-shake and throttling is to prevent functions from being called many times in high-frequency events, which is a performance optimization solution.

The difference is that the anti-shake function only calls once n milliseconds after the high frequency event ends, while the throttling function calls once every n milliseconds during the high frequency event.

Image stabilization function

The function is executed only once after the high-frequency event is triggered. If the high-frequency event is triggered again within the specified time (delay), the timer is reset until the high-frequency event stops triggering.

// When the Scroll event is continuously triggered, the handle event handler will only be called once after scroll stops for 1000 milliseconds. In other words, the Handle event handler will not be executed during the continuous scroll event.
/ * * *@description: anti-shake function *@param {Function} Fn the function that needs to be buffered *@param {Number} Delay Delay of anti-shake (ms) *@return {Function} * /
function debounce(fn, delay) {
  let timeout = null;
  return function () {
    let context = this;
    let args = arguments;
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() = >{ fn.apply(context, args); }, delay); }}function handle() {
  console.log(Math.random());
}
window.addEventListener('scroll', debounce(handle, 1000));
Copy the code

Throttling function

Specifies that a function can fire only once per unit of time. If the function fires more than once in a unit of time, only one time is effective, meaning that it fires once every period of time.

Use the timestamp mode

  • When a high frequency event is triggered, the first one is executed immediately (the interval between the mousemove event binding function and the actual event is usually greater than delay, but if you have to scroll the page within 1000 ms of loading…
  • However frequently events are triggered, they are executed only once per delay time. When the last event is triggered, the event will not be executed
/ * * *@description: Throttling function (timestamp mode) *@param {Function} Fn the function that requires throttling processing *@param {Number} Delay Throttling interval (milliseconds) *@return {Function} * /
function throttle(fn, delay) {
  let prev = Date.now();
  return function() {
    let context = this; // This refers to the DOM element that listens for the onMousemove event
    let args = arguments; // Arguments is the event passed in by the onMousemove event
    let now = Date.now();
    if (now - prev >= delay) {
      fn.apply(context, args);
      prev = Date.now(); }}; };function handle() {
  console.log(Math.random());
  // console.log('this', this);
  // console.log('args', arguments);
}
window.addEventListener('scroll', throttle(handle, 1000));
Copy the code

Using timer Mode

  • When the event is triggered, we set a timer. When the event is triggered again, if the timer exists, it is not executed until after the delay time, the timer executes the execution function and clears the timer, so that the next timer can be set.
  • When the event is first raised, the function is not executed immediately, but after a delay of seconds. No matter how frequently events are triggered, they are executed only once per delay time.
  • After the last stop, the function may be executed again due to timer delay
function throttle(fn, delay) {
  let timer = null;
  return function() {
    let context = this;
    let args = arguments;
    if(! timer) { timer =setTimeout(function() {
        fn.apply(context, args);
        timer = null; }, delay); }}; };function handle() {
  console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
Copy the code

Use the timestamp + timer mode

  • Inside the throttling function, startTime, curTime and delay are used to calculate remaining time.
  • whenremaining <= 0Is the time to execute the event handler (ensuring that the event handler can be executed immediately after the first event and every delay time).
  • If the remaining time is not reached, the remaining time is set to the remaining time. This ensures that the remaining event handler can be executed after the last event.
  • Of course, if remaining fires another event, the timer is cancelled and a remaining is recalculated to determine the current state.
function throttle(fn, delay) {
  let timer = null;
  let startTime = Date.now();
  return function() {
    let curTime = Date.now();
    let remaining = delay - (curTime - startTime);
    let context = this;
    let args = arguments;
    clearTimeout(timer);
    if (remaining <= 0) {
      fn.apply(context, args);
      startTime = Date.now();
    } else {
      timer = setTimeout(() = >fn.apply(context, args), remaining); }}; };function handle() {
  console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
Copy the code

Application scenarios

Common application scenarios are in the process of continuously triggering high-frequency events to perform certain operations, such as resize and Scroll events for window objects, mousemove events for dragging, text input, keyup events for automatic completion, and so on.

Anti-shake application scenarios

  • Scroll Event Indicates the event that the scroll is triggered
  • Search box input query, if the user has been in the input, there is no need to keep calling to request the server interface, and so on when the user stops input, request interface; Set a proper interval to reduce the pressure on the server.
  • Form input validation
  • Button submit event
  • Browser window resize events (such as recalculating the layout after the window stops resizing).

Application scenarios of throttling

  • DOM element drag and drop implementation (Mousemove)
  • Search for Associations (KeyUp)
  • Calculate the distance of mouse movement (Mousemove)
  • Canvas Drawing Board (Mousemove)
  • Shooter mouseDown/KeyDown/Touch events (only one shot per unit of time)
  • Listen for scroll events to see if more load automatically at the bottom of the page

The sample

<html>
  <div id="demo">
    <p>Note: Mouse moves in the following elements, respectively, to join the anti-shake and throttling function.</p>

    <h2>Image stabilization</h2>
    <p>Execute a numeric accumulation event 1000ms after the mouse stops moving.</p>
    <div class="content" id="content">0</div>
    <h2>The throttle</h2>
    <p>During mouse movement, a numeric accumulation event is executed every 1000ms.</p>
    <div class="content" id="content2">0</div>
  </div>
</html>

<style>
#demo .content{
  height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;
}
#demo h2{margin: 10px 0; }#demo p{color:# 666;margin:0; }</style>

<script>
// The buffeting function
function debounce(fn, wait) {
  let timeout = null;
  return function () {
    let context = this;
    let args = arguments;
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() = >{ fn.apply(context, args); }, wait); }}// test debounce
let num = 1;
let content = document.getElementById('content');
function count() {
  content.innerHTML = num++;
}
content.onmousemove = debounce(count, 1000);



// throttling function
function throttle(fn, delay) {
  let timeout = null;
  return function () {
    let context = this;
    let args = arguments;
    if(! timeout) { timeout =setTimeout(() = > {
        fn.apply(context, args);
        timeout = null;
      }, delay)
    }
  }
}

// test throttle
let num2 = 1;
let content2 = document.getElementById('content2');
function count2() {
  content2.innerHTML = num2++;
}
content2.onmousemove = throttle(count2, 1000);
</script>
Copy the code

Anti-shake and throttling are used in vUE projects

As you can see from the debounce and Throttle methods defined earlier, they both return a function, and we end up calling the returned function. So in a Vue project, the target function can be debounce or throttle bound first in the Created life cycle, for example 🌰

<template> <div class="wrapper" @click="clickDebounce"> <! --> </div> </template> <script> export default {data() {return {count: 0 } }, created() { this.clickDebounce = this.debounce(this.clickHandler, 1500); }, methods: { debounce(fn, delay) { let timeout = null; return function () { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { fn.apply(context, args); }, delay); }}, clickHandler(e) {console.log(' clickHandler 'for ${++this.count}); }, } } </script> <style scope> .wrapper { height: 50px; border: 1px solid red; } </style>Copy the code

Of course, we can also use a throttle-throttling package directly, such as the throttle-Debounce package referenced in the Select component of Element-UI

It is used in the same way, except that Debounce and Throttle are used according to the specification of the plug-in.

<template>... </template> <script> import debounce from 'throttle-debounce/debounce'; export default { data() { return { // ... } }, created() { this.debouncedOnInputChange = debounce(this.debounce, () => { this.onInputChange(); }); this.debouncedQueryChange = debounce(this.debounce, (e) => { this.handleQueryChange(e.target.value); }); }, methods: { onInputChange() { if (this.filterable && this.query ! == this.selectedLabel) { this.query = this.selectedLabel; this.handleQueryChange(this.query); } }, handleQueryChange(val) { // ... }, } } </script>Copy the code