“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

Promises:

What is shaking? It’s like a cannon, dropping a bomb, loading a bomb. That function, once fired, executes once.

So, what is shockproof? It’s like a machine gun, tuk-tuk-tuk, no matter how many times you shoot it, you reload the tank. High frequency trigger function, the time interval will be recalculated. When the function is fired for the last time (the last bullet is fired), the time arrives to execute once.

Said one words:

The event response function executes at a specified time (before/after). If the trigger is triggered again within the specified time, the time is recalculated.

At the beginning of appearance:

<div class="box"></div>
<button id="btn">Cancel the offer</button>
<script>
  let obox = document.querySelector('.box')
  let count = 0
  obox.innerHTML = count
  obox.onmousemove = function () {
    obox.innerHTML = count++
    console.log(count);
  }
</script>
Copy the code

When the mouse moves n times, it triggers n times.

Rectification appearance:

/ / < script SRC = "https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js" > < / script >
/ / or
<script src="https://cdn.jsdelivr.net/npm/[email protected]/underscore-umd-min.js"></script>
<script>
let obox = document.querySelector('.box')
let count = 0
obox.innerHTML = count
function todo(e) {
  obox.innerHTML = ++count
  console.log(e);
}
obox.onmousemove = _.debounce(todo, 1000)
</script> 
Copy the code

Just use the douche-proof functions in lodash.js or underscore. Js directly and you can make your frantic mouse movement happen once for 1s.

Make a shape

It’s not enough for us to know what it is; We need to know why! Without further ado, let’s invent one out of thin air!

In terms of underscore, analyze the Debrenounce function first. It takes three arguments: fun, the jitter prevention function, the number of milliseconds to be delayed, wait, and whether to execute immediate immediately.

The first edition

Let’s do the same thing with the parameters. First, when the mouse moves, it receives a function, so it needs to return a function; Second, it needs to wait for the specified time to execute, and it needs a timer.

function debounce(fn, wait = 200, immediate = false) {
  let timer = null
  return function () {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() = > {
      fn()
    }, wait)
  }
}
Copy the code

You can use the setTimeout timer to execute a function once within a certain period of time. So the most basic anti – shake function 🆗 pull!

The second edition

Not only do we need to consider function functions, but we also need to consider the possibility of fn using event events and internal this pointing issues when executing function functions. In addition, the first release is only done after execution, we also need to complete the immediate implementation of the functionality.

let obox = document.querySelector('.box')
let count = 0
obox.innerHTML = count
function todo(e) {
  obox.innerHTML = ++count
  console.log(this, e);
}
obox.onmousemove = _.debounce(todo, 1000.true)
// <div class="box">1</div>
// MouseEvent{isTruted: true, screenX: 87, screenY: 388, clientX: 68, clientY: 295,... }
Copy the code

In our first version this pointed to window, and e was undefined. In our custom debounce function, we find that the returned function this points to div, so we need to change this to when fn executes.

Consider parameter passing. You can receive parameters in the return function and pass them in when the function is executed.

function debounce(fn, wait = 200, immediate = false) {
  let timer = null
  return function (. args) {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() = > {
      fn.apply(this, args)
    }, wait)
  }
}
Copy the code

In addition, we also need to consider whether to implement immediately, and the third parameter.

If the argument immediate is true, then fn is executed; If false, it should be executed after a certain amount of time (using setTimeout).

Use immediate to determine immediate execution: When immediate execution occurs, the function must be executed without a timer. Wait for 2 seconds to clear the timer and wait for the next execution.

function debounce(fn, wait = 200, immediate = false) {
  let timer = null, result
  return function (. args) {
    if (timer) clearTimeout(timer)
    if (immediate) {// Execute immediately(! timer) && fn.apply(this, args)  // Execute at the beginning, with no timing
      timer = setTimeout(() = > {
        timer = null
      }, wait)
    } else {/ / after the implementation
      timer = setTimeout(() = > {
        fn.apply(this, args)
      }, wait)
    }
  }
}
Copy the code

In addition, it can be stored in variables to record the order of execution.

function debounce(fn, wait = 200, immediate = false) {
  let timer = null
  let isEnd = true // Execute by default
  return function (. args) {
    if (timer) clearTimeout(timer)
    if (immediate) { / / perform first
      isEnd && fn.apply(this, args)
      isEnd = false
    }
    timer = setTimeout(() = > {
      (!immediate) && fn.apply(this, args) / / after the implementation
      isEnd = true
    }, wait)
  }
}
Copy the code

The third edition

Based on the second version, we can add function return values and methods to cancel jitter. Add a function return value that records the value of the function that was executed, either immediately or later, and returns the same value.

function debounce(fn, wait = 200, immediate = false) {
  let timer = null, isEnd = true, result
  let debounced = function (. args) {
    if (timer) clearTimeout(timer)
    if (immediate) {
      isEnd && (result = fn.apply(this, args))
      isEnd = false
    }
    timer = setTimeout(() = > {
      (!immediate) && (result = fn.apply(this, args))
      isEnd = true
    }, wait)
    return result
  }
  return debounced
}
Copy the code

Use result to record the return value and then return it. The code above has been changed a little to record the entire return function as a variable and return that variable. This makes it easier to add an unjitter method to the function later.

function debounce(fn, wait = 200, immediate = false) {
  let timer = null, isEnd = true, result
  let debounced = function (. args) {
    if (timer) clearTimeout(timer)
    if (immediate) {
      isEnd && (result = fn.apply(this, args))
      isEnd = false
    }
    timer = setTimeout(() = > {
      (!immediate) && (result = fn.apply(this, args))
      isEnd = true
    }, wait)
    return result
  }
  debounced.cancel = function () {
    if (timer) clearTimeout(timer)
    timer = null
  }
  return debounced
}
Copy the code

In the cancel method, the jitter timer is directly cleared and the variable is reclaimed.

Function return value asynchronous problem

Thanks to the reader’s suggestions, I did find out after using underscore that I did have asynchronous issues with the return values I received.

let obox = document.querySelector('.box')
let obtn = document.querySelector('#btn')
let count = 0
function todo(e) {
  obox.innerHTML = ++count
  console.log(this, e);
  return count
}
let debounceFn = _.debounce(todo, 1000.false)
obox.onmousemove = (e) = > {
  let value = debounceFn(e)
  console.log(value);
}
Copy the code

When I enter div for the first time, I execute the todo function once, and the return value count should be 1, but the actual output is undefined. On the second entry, the output is 1, but the page count is 2. The return value returns the previous return value.To solve the asynchronous problem, we can use a Promise.

function debounce(fn, wait, immediate) {
  let timer = null, result
  let debounced = function (. args) {
    return new Promise(res= > {
      if (timer) clearInterval(timer)
      if (immediate) {// Execute immediately
        if(! timer) { result = fn.apply(this, args)
          res(result)
        }
        timer = setTimeout(() = > {
          timer = null
        }, wait);
      } else {
        timer = setTimeout(() = > {
          result = fn.apply(this, args)
          res(result)
        }, wait);
      }
    })
  }
  debounced.cancel = function () {
    if (timer) clearTimeout(timer)
    timer = null
  }
  return debounced
}
let obox = document.querySelector('.box')
let obtn = document.querySelector('#btn')
let count = 0
function todo(e) {
  obox.innerHTML = ++count
  console.log(this, e);
  return count
}
let debounceFn = debounce(todo, 1000.false)
obox.onmousemove = async (e) => {
  try {
    let value = await debounceFn(e)
    console.log(value);
  } catch (e) {
    console.log(e); }}Copy the code

Use promises to solve the problem of return value asynchrony, which is synchronized with async/await when called. Enter div, call once, output value 1, call twice, output value 2, return value synchronization.

What’s the use of

The most common application of anti – shaking is to solve the problem of frequently accessing interfaces. To summarize the common applications:

  • Prevent forms from being submitted multiple times

  • Search box input query (listen to the input box input content, set the interface access at intervals)

  • Scroll trigger

  • The resize event occurred when the browser window was scaled

review

The stabilization function involves apply changes to this binding, closures, and so on. Here’s a review of the passage:

  • Call, apply, bind implementation principle

  • Read the closure