防抖 (debounce)

函数的防抖:对于频繁触发某个操作,我们只识别一次(只触发执行一次函数)
inde.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scal=1.0">
  6. <title>防抖</title>
  7. <style>
  8. * {
  9. margin: 0;
  10. padding: 0;
  11. }
  12. html,body {
  13. height: 150%;
  14. background: -webkit-linear-gradient(top left, lightblue, lightgreen, orange);
  15. }
  16. #submit {
  17. margin: 50px;
  18. }
  19. </style>
  20. </head>
  21. <body>
  22. <button id="submit">提交按钮</button>
  23. <script src="debounce.js"></script>
  24. </body>
  25. </html>

debounce.js

  1. function handle() {
  2. console.log(submit)
  3. // 设置定时器
  4. setTimeout(()=> {
  5. console.log("OK");
  6. }, 1000);
  7. }
  8. submit.onclick = handle;

疯狂点击按钮的时候,执行很多次handle,对于这种频繁操作,希望只触发一次操作。

  1. /**
  2. *
  3. * @param {*} func[function] 要执行的方法
  4. * @param {*} wait[number] 等待执行的时间(用户自己规定多长时间内不算频繁的操作)
  5. * @param {*} immediate 疯狂点击的时候最终只识别一次,识别第一次还是最后一次,immediate为true,立即执行(第一次)
  6. * @return 可以被调用执行的函数
  7. */
  8. // 在当前点击完成后,在wait时间内,是否还会触发第二次,如果没有触发第二次,属于非频繁操作,直接执行想要执行的函数func;如果触发了第二次,则clear之前的时间,从当前这次再开始等待...
  9. function debounce(func, wait = 300, immediate = false) {
  10. let timer = null;
  11. return function (...args) {
  12. // let now = immediate && timer === null;
  13. let now = immediate && !timer;
  14. // 每次点击都把之前设置的定时器清除
  15. clearTimeout(timer);
  16. timer = setTimeout(() => {
  17. // 手动让其回归到初始状态(下一轮开始还是从timer为null开始),没加这句,只会执行一次,就没有了
  18. timer = null;
  19. immediate ? null : func.call(this, ...args);
  20. }, wait);
  21. // 如果是立即执行
  22. now ? func.call(this, ...args) : null;
  23. };
  24. }
  25. // function handle() {
  26. // console.log(submit)
  27. // // 设置定时器
  28. // setTimeout(()=> {
  29. // console.log("OK");
  30. // }, 1000);
  31. // }
  32. function handle() {
  33. console.log("OK");
  34. }
  35. // submit.onclick = handle; // 点击一次触发执行一次handle,频繁触发,频繁执行handle
  36. // 原理:在匿名函数中,控制handle只执行一次
  37. submit.onclick = debounce(handle, 3000, true);

clearTimeout(timer) 是清除定时器

节流 (throttle)

函数节流: 在一段频繁操作中,可以出发多次(不像防抖,只一次),但是触发的频率自己指定。

  1. function handle() {
  2. // 设置定时器
  3. console.log('OK');
  4. }
  5. window.onscroll = handle;

现象:随着页面的滚动,一直打印字符串’ok’。
每一次滚动过程中,浏览器有最快反应时间(5~6ms, ie:13~17ms),只要反应过来就会触发一次函数(此时
image.png
利用throttle控制频率为300ms触发一次。

  1. /**
  2. * 函数节流:在一段频繁操作中,可以触发多次,但是触发的频率由自己指定
  3. * @param {*} func[function]:最后要触发执行的函数
  4. * @param {*} wait[number]:触发的频率
  5. */
  6. function throttle(func, wait = 300) {
  7. let timer = null,
  8. previous = 0; // 记录上一次操作的时间
  9. // return出匿名函数,相当于每隔5~6ms触发一次匿名函数,我们可以在匿名函数中控制handle的频率
  10. return function () {
  11. let now = new Date(),
  12. remaining = wait - (now - previous); // 记录还差多长时间达到我们一次触发的频率
  13. if (remaining <= 0) {
  14. window.clearTimeout(timer); // 快满足300ms时,remaining就会小于0,会执行func,这时候定时器里的待执行func不能再执行,需要clear
  15. timer = null;
  16. previous = now;
  17. // 俩次操作的间隔时间已经超过wait了
  18. func, call(this, ...params);
  19. } else if (!timer) {
  20. // timer === null timer不存在,还没设置,开始启动定时器
  21. // 俩次操作的间隔时间不符合触发的频率
  22. timer = setTimeout(() => {
  23. timer = null;
  24. previous = new Data();
  25. func.call(this, ...params);
  26. }, remaining);
  27. }
  28. };
  29. }
  30. window.onscroll = throttle(handle);

=============================================================================

防抖 (debounce)

防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。
想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢?

  • 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
  • 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
  • 文本编辑器实时保存,当无任何更改操作一秒后进行保存

代码如下,可以看出来防抖重在清零 clearTimeout(timer)

  1. function debounce (f, wait) {
  2. let timer
  3. return (...args) => {
  4. clearTimeout(timer)
  5. timer = setTimeout(() => {
  6. f(...args)
  7. }, wait)
  8. }
  9. }

节流 (throttle)

节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。

  • scroll 事件,每隔一秒计算一次位置信息等
  • 浏览器播放事件,每个一秒计算一次进度信息等
  • input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)

代码如下,可以看出来节流重在开关锁 timer=null

  1. function throttle (f, wait) {
  2. let timer
  3. return (...args) => {
  4. if (timer) { return }
  5. timer = setTimeout(() => {
  6. f(...args)
  7. timer = null
  8. }, wait)
  9. }
  10. }

总结 (简要答案)

  • 防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout
  • 节流:控制流量,单位时间内事件只能触发一次,如果服务器端的限流即 Rate Limit。代码实现重在开锁关锁 timer=timeout; timer=null

摘录文章:https://segmentfault.com/a/1190000023127030