preface

Shockstabilization and throttling come into play when we need some way to avoid frequent callbacks that cause a lot of computation or requests.

Both of these things exist in the form of closures.

Image stabilization (debounce)

  • The event is raised for n seconds before the callback is executed. If it is called again within n seconds, the time is re-timed.

The throttle (throttle)

  • The function can only be fired once per unit of time. If this function fires multiple times per unit time, only one will take effect. (The command is executed once every n seconds, and the command is executed once every n seconds……)

Buffering is instant and non – instant, and throttling is time – stamped and timed.

Image stabilization (debounce)

1. The stabilization is not performed immediately

<div>debounce:<input type="text" id="debounce-input" /></div>
Copy the code
const inputDom = document.getElementById('debounce-input')

function debounce(func, wait) {
  let timeout
  return function () {
    const that = this // Change the reference to this inside the execution function
    const args = arguments // Solve the doSomeThing event pointing problem
    clearTimeout(timeout)
    timeout = setTimeout(function () {
      func.apply(that, args)
    }, wait)
  }
}

function doSomeThing(e) {
  console.log('I'm shaking.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

inputDom.onkeyup = debounce(doSomeThing, 300)
Copy the code

2. Perform anti-shaking immediately

<div>debounce:<input type="text" id="debounce-input" /></div>
Copy the code
const inputDom = document.getElementById('debounce-input')

function debounce(func, wait, immediate) {
  // immediate Whether to execute immediately
  let timeout
  return function () {
    const that = this // Change the reference to this inside the execution function
    const args = arguments // Solve the doSomeThing event pointing problem
    clearTimeout(timeout) // Clear the last setTimeout each time you come in
    if (immediate) {
      constcallNow = ! timeout// Need a condition to determine whether to execute immediately
      timeout = setTimeout(function () {
        timeout = null
      }, wait)
      // Execute immediately
      if (callNow) func.apply(that, args)
    } else {
      // Not immediately executed
      timeout = setTimeout(function () {
        func.apply(that, args)
      }, wait)
    }
  }
}

function doSomeThing(e) {
  console.log('I'm shaking.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

inputDom.onkeyup = debounce(doSomeThing, 300.true)	
Copy the code

3. The return value is required

<div>debounce:<input type="text" id="debounce-input" /></div>
Copy the code
const inputDom = document.getElementById('debounce-input')

function debounce(func, wait) {
  let timeout
  let result // Return the result
  return function () {
    const that = this // Change the reference to this inside the execution function
    const args = arguments // Solve the doSomeThing event pointing problem
    clearTimeout(timeout)
    timeout = setTimeout(function () {
      result = func.apply(that, args)
    }, wait)
    return result
  }
}

function doSomeThing(e) {
  console.log('I'm shaking.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests

  return 'Desired outcome'
}

inputDom.onkeyup = debounce(doSomeThing, 300.true)
Copy the code

4. Cancel shaking

 <div>
   debounce:<input type="text" id="debounce-input" />
   <button id="cancel-btn">Cancel the offer</button>
</div>
Copy the code
const inputDom = document.getElementById('debounce-input')
const cancelBtnDom = document.getElementById('cancel-btn')

function debounce(func, wait) {
  let timeout
  let debounced = function () {
    const that = this // Change the reference to this inside the execution function
    const args = arguments // Solve the doSomeThing event pointing problem
    clearTimeout(timeout)
    timeout = setTimeout(function () {
      func.apply(that, args)
    }, wait)
  }
  debounced.cancel = function () {
    // Add the cancellation method
    clearTimeout(timeout)
    timeout = null
  }

  return debounced
}

function doSomeThing(e) {
  console.log('I'm shaking.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

const doDebounce = debounce(doSomeThing, 1000.true)

inputDom.onkeyup = doDebounce

cancelBtnDom.onclick = function () {
  doDebounce.cancel()
}
Copy the code

5. Application Scenarios:

  • The Scroll event is triggered
  • Enter a query in the search box
  • Form validation
  • Button submit event
  • Browser window zooming
  • .

The throttle (throttle)

1. Use the time stamp

<div style="height: 10000px"></div>
Copy the code
// The first time is executed immediately, and the last time is not called to trigger execution
function throttle(func, wait) {
  let old = 0 // The previous timestamp
  return function () {
    const that = this
    const args = arguments
    let now = new Date().valueOf() // Get the current timestamp
    if (now - old > wait) {
      func.apply(that, args) // Execute immediately
      old = now
    }
  }
}

function doSomeThing(e) {
  console.log('I'm saving money.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

document.onscroll = throttle(doSomeThing, 500)
Copy the code

2. Use a timer

<div style="height: 10000px"></div>
Copy the code
// The first time is not immediately executed, the last time will be called to trigger execution
function throttle(func, wait) {
  let timeout
  return function () {
    const that = this
    const args = arguments
    if(! timeout) { timeout =setTimeout(function () {
        func.apply(that, args)
        timeout = null
      }, wait)
    }
  }
}

function doSomeThing(e) {
  console.log('I'm saving money.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

document.onscroll = throttle(doSomeThing, 500)
Copy the code

3. Time stamp + timer

<div style="height: 10000px"></div>
Copy the code
// The first time is executed immediately, and the last time is invoked to trigger execution
function throttle(func, wait) {
  let timeout
  let old = 0 // The previous timestamp

  return function () {
    const that = this
    const args = arguments
    let now = +new Date(a)// Get the current timestamp
    if (now - old > wait) {
      // The first time will be executed immediately
      if (timeout) {
        clearTimeout(timeout)
        timeout = null
      }
      func.apply(that, args) // Execute immediately
      old = now
    } else if(! timeout) {// The last time will be executed
      timeout = setTimeout(function () {
        func.apply(that, args)
        old = +new Date()
        timeout = null
      }, wait)
    }
  }
}

function doSomeThing(e) {
  console.log('I'm saving money.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

document.onscroll = throttle(doSomeThing, 500)
Copy the code

The throttling function is not immediately executed the first time, and the last time is not triggered by the call.

4. Optimize the throttle function

<div style="height: 10000px"></div>
Copy the code
function throttle(func, wait, options) {
  let timeout
  let old = 0 // The previous timestamp
  if(! options) options = {}return function () {
    const that = this
    const args = arguments
    let now = new Date().valueOf() // Get the current timestamp
    if (options.leading === false && !old) { // Let the first time not execute
      old = now
    }
    if (now - old > wait) {
      // The first time will be executed immediately
      if (timeout) {
        clearTimeout(timeout)
        timeout = null
      }
      func.apply(that, args) // Execute immediately
      old = now
    } else if(! timeout && options.trailing ! = =false) {
      // The last time will be executed
      timeout = setTimeout(function () {
        func.apply(that, args)
        old = new Date().valueOf()
        timeout = null
      }, wait)
    }
  }
}

function doSomeThing(e) {
  console.log('I'm saving money.')
  // console.log(e)
  // console.log(this);
  // There may be callbacks or Ajax requests
}

/ * * the first immediate execution, the last time will not be called {leading: true, trailing: false} * not executed immediately for the first time, the last time is called {leading: false, trailing: true} * immediately executed for the first time, The last time is called {leading: true, trailing: true} * options = {} leading: XXX, trailing: XXX; The default options for {leading: true, trailing: true} * throttle (doSomeThing, wait, options) * /
document.onscroll = throttle(doSomeThing, 500)
Copy the code

5. Disable throttling

Same as removing shaking.

6. Pay attention to

  • now-old > waitSometimes the computer has problems with local time,new Date()Is not allowed.

7. Application scenario

  • Monitor scroll scroll events;

  • DOM element drag-and-drop function implementation;

  • Shooting games;

  • Calculate the distance the mouse moves;

conclusion

  • Function stabilization and function throttling both prevent frequent firing at one time, but the two principles are different.
  • Function buffering is executed only once in a certain period of time, while function throttling is executed at intervals.
  • Actual production or uselodashTo achieve reliable anti-shaking, throttling to achieve 🤣.