3
头图

What exactly are anti-shake and throttling?

Anti-shake and throttling belong to the knowledge of performance optimization, which can effectively reduce the number of executions of the methods you define when high-frequency events are triggered.

Still not feeling it? ? ? So, look at the following scenario:

  1. The user enters a keyword in the search box (only when he completes the input, we send a request to the server, and then give the search result)
  2. Automatically save form data filled in by users

The above scenarios all correspond to a high-frequency event, that is, the onKeyUp event of input or textarea. We generally send a request to the server after the user triggers this event (the advantage of this is that the user does not need to click the search button, there is a real-time query a feeling of).

Then the question comes , when the user enters a keyword to be queried, it may be necessary to press and lift the keys of the keyboard multiple times. Do we have to request the server every time onKeyUp? Obviously not elegant (because if there are a large number of users searching at the same time, the server will be under a lot of pressure). And debounce (debounce) is to solve similar problems.

We often encounter similar events in browsers (such as browser scroll, resize, mousemove...) Next, we use the automatic save scene to illustrate how to implement anti-shake in JavaScript.

Scenario description : After the user enters text in the textarea, it needs to be automatically saved to the server for him (which can be understood as saving as a draft). At this time, what we need to do is to optimize the number of requests to the server, and we need to use the anti-shake function.

Example demonstration : The article provides the address of the online demonstration, which is convenient for intuitive understanding of the role of anti-shake and throttling:

Anti-shake function

Let's take a look at a common wrong spelling first, pay attention! ! ! Many of the results found in Baidu are like this. After using it, you will find that your function will still be executed immediately and will not be delayed.

 function debounce(fn, delay) {
    let timer = null
    return function (args) {
        if (timer) {
            clearTimeout(timer) 
        }
        timer = setTimeout(fn.call(this, args), delay)
    }
}

The problem is in the line timer = setTimeout(fn.call(this, args), delay) .

Modify it to the following, and it can be executed according to the set delay delay:

 function debounce(fn, delay) {
    let timer = null
    return function (args) {
        if (timer) {
            clearTimeout(timer) 
        }
        timer = setTimeout(function() {
            fn.call(this, args)
        }, delay)
    }
}

// 或者
function debounce(fn, delay) {
  let timer = null
    return function (args) {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(function() {
        fn(args)
      }, delay)
    }
}

Don't underestimate this small difference, it may waste a lot of your time and make you doubt about image stabilization...

A complete example is posted below, as well as the anti-shake online demo address , so that you can better understand this scene.

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖和节流</title>
    <style>
        .de_wrapper {
          padding: 20px;
          display: flex;
        }
        .col {
            width: 40%;
        }
        .log {
          height: 300px;
          overflow-y: scroll;
          background-color: #fff;
        }
    </style>
</head>
<body>
<div class="de_wrapper">
    <div class="col">
        <h3>未使用防抖(每次按键抬起都会触发保存)</h3>
        <textarea name="" id="1" cols="30" rows="10" onKeyUp="printLog(event)"></textarea>
        <div id="log" class="log"></div>
    </div>
    <div class="col">
        <h3>使用防抖(停止输入2秒后保存)</h3>
        <!-- <textarea name="" id="2" cols="30" rows="10"></textarea> -->
        <textarea name="" id="2" cols="30" rows="10" onKeyUp="debounceLog(event)"></textarea>
        <div id="log1" class="log"></div>
    </div>
</div>
<script>
    let log = null
    let log1 = null

    window.onload = function() {
        log = document.getElementById('log')
        log1 = document.getElementById('log1')

        // 写法1
        // document.getElementById('2').addEventListener('keyup', function(e) {
        //     debounceLog(e)
        // })

        // 写法2
        // document.getElementById('2').addEventListener('keyup', debounceLog)

        // 写法3
        // document.getElementById('2').addEventListener('keyup', debounce(printDebounceLog, 2000))

    } 

    function printLog(e) {
        log.innerText += `keyup 事件触发【请求服务器保存数据...】: ${e.target.value}\n`
    }

    function printDebounceLog(e) {
        log1.innerText += `keyup 事件触发【请求服务器保存数据...】: ${e.target.value}\n`
    }

    let debounceLog = debounce(printDebounceLog, 2000)

    function debounce(fn, delay) {
      let timer = null
        return function (args) {
          if (timer) {
            clearTimeout(timer)
          }
          timer = setTimeout(function() {
            fn(args)
          }, delay)
        }
    }
</script>
</body>
</html>

Throttle function

The difference between the throttle function and the anti-shake function: no matter how frequently the event is triggered, the function throttle will only execute the callback once within a certain period of time; while the function anti-shake is the last trigger callback of the high-frequency event.

Throttling function usage scenario: A very vivid example is mousedown firing bullets, only one bullet can be fired per second, online demo address .

 function throttle(fn, limit) {
  let lastTime
  return function(args) {
    if (!lastTime) {
      fn.apply(this,args)
      lastTime = Date.now()
    } else {
      if ((Date.now() - lastTime) >= limit) {
        fn.apply(this,args)
        lastTime = Date.now()
      }
    }
  }
}
 // 页面结构
<div class="de_wrapper">
    <div class="col">
        <h3>未使用节流(点击按钮可以疯狂发射子弹)</h3>
        <div class="sky"></div>
        <button class="fire_btn">发射</button>
      </div>
      <div class="col">
        <h3>使用节流(发射子弹速度会被限制)</h3>
        <div class="sky"></div>
        <button class="fire_btn">发射</button>
    </div>
</div>

<script>

let sky = null
let sky1 = null
let btn = null
let btn1 = null

window.onload = function() { 
    sky = document.querySelectorAll('.sky')[0]
    sky1 = document.querySelectorAll('.sky')[1]
    btn = document.querySelectorAll('.fire_btn')[0]
    btn1 = document.querySelectorAll('.fire_btn')[1]

    btn.addEventListener('click', fire)
    btn1.addEventListener('click', throttle(t_fire, 1000))
} 

function fire() {
  const b = document.createElement('span')
  b.classList.add('bullet')
  sky.appendChild(b)
  setTimeout(() => {
    sky.removeChild(b)
  }, 1000)
}

function t_fire() {
  const b = document.createElement('span')
  b.classList.add('bullet')
  sky1.appendChild(b)
  setTimeout(() => {
    sky1.removeChild(b)
  }, 1000)
}

function throttle(fn, limit) {
  let lastTime
  return function(args) {
    if (!lastTime) {
      fn.apply(this,args)
      lastTime = Date.now()
    } else {
      if ((Date.now() - lastTime) >= limit) {
        fn.apply(this,args)
        lastTime = Date.now()
      }
    }
  }
}

Summarize

  1. Function anti-shake: Combine multiple operations into one operation. The principle is to maintain a timer, and the timer set later will replace the previous timer. If high-frequency events are always triggered, the callback function will not be executed.
  2. Function throttling: Make the function trigger only once in a certain period of time. The principle is to judge whether the time limit is met, and then execute.

IICCOM-Personal Blog|Technical Blog "Anti-Shake and Throttling"


来了老弟
508 声望31 粉丝

纸上得来终觉浅,绝知此事要躬行


引用和评论

1 篇内容引用
0 条评论