利用 debounce/throttle 函数减轻对后端接口的压力

前言

在前端开发过程中,偶尔会遇到类似点赞、抢购、切换之类的按钮。这类按钮一般会绑定一些需要和后端进行交互的事件。如果不对这些事件做限制处理,那么当用户量激增时无疑对后端的压力也会成倍增加。
一名优雅的前端工程师不仅要能对自己的展示页面负责,也要对后端接口的流量负责。此时我们就得思考,如何使用一些前端上的技巧减轻用户操作对后端的压力。

这里很简单的介绍两种函数: debouncethrottle

debounce

debounce 函数起到的作用是能防止用户的快速切换操作。
打个简单的比方。页面上有这三个按钮:
按钮
如果不做 debounce 操作,每次点击按钮都会给后端发一次请求,如果有用户来回不断的点击按钮,效果会很不好。
加上 debounce 操作之后,只有用户在点击某个按钮并停留 500ms 之后,才会给后端发请求。
这里我简单模拟了一个页面,大家可以体验一下。上方是未做 debounce 操作的,下方是做了 debounce 操作的。
简单的模拟页面

debounce 的实现方式

debounce 函数其实很好实现。用大白话说就是:在执行函数的时候先清除定时器,然后设置一个 x 秒后运行的定时器,执行我们想要执行的方法。

这是我的简单实现

function debounce(func, timer) {
  let timeoutId = 0; // 定时器 id
  return function f() {
    const context = this; // 获取当前的 this
    const args = arguments; // 获取传入的参数, event 之类
    clearTimeout(timeoutId); // 清除定时器
    timeoutId = setTimeout(function() {
      func.apply(context, args);
    }, timer);
  }
}

这里用了闭包能保存函数运行状态的特性保存住了定时器的 ID。
在调用时,可以这样写

document.addEventListener('click', debounce(func, 500)); // func为我们想执行的方法, 500为时间间隔,单位ms

在上例中,我就是这样给按钮绑定事件的:

const func = function(e) {
  const msg = e.target.innerText;
  op.innerHTML += `<p>${msg}被点击了, 给后端发了一条查询请求</p>`;
}
container.querySelector('.m-tab').addEventListener('click', debounce(func, 500));

throttle

throttle 函数起到的作用是防止用户快速重复操作。
打个简单的比方。页面上有一个点赞按钮,每点一次就会给后端发一次请求。有的用户可能会疯狂点赞,为了防止这类用户的这类行为给后端造成很大的压力,我们必须在前端限制点击事件的执行频率。
这里有一个简单的 demo 页面
上方是未做 throttle 操作的,下方是做了 throttle 操作的。

throttle 的实现方式

实现方式用大白话说就是:使用时间戳,当用户出发事件时,拿到取出当前时间戳,然后减去之前设置的时间戳。如果差值大于程序设置的时间间隔,就执行函数,然后更新之前的时间戳为现在的时间戳。

说起来好像有点绕…直接看我的代码吧。

function throttle(func, timer) {
  let preview = 0;    // 之前的时间戳
  let context, args;
  return function f() {
    let now = +new Date(); // 现在的时间戳
    context = this;
    args = arguments;

    if (now - preview > timer) {  // 如果差值大于程序设置的时间间隔,就执行函数
      func.apply(context, args);
      preview = now; // 更新之前的时间戳为现在的时间戳
    }
  }
}

示例页面中,按钮的点击事件是这样绑定的

const func = function(e) {
  op.innerHTML += '<p> 点了一次赞 </p>';
}
container2.querySelector('button').addEventListener('click', throttle(func, 400));

可以看出,作为前端不仅要考虑页面整体的样式布局,更多的应该为整个项目考虑。
一名合格的前端必须知道页面上的风险点和需要容灾的位置并提前做出规避操作。如何成为一名合格的前端呢?
多踩坑,踩的坑越多,进步的就越大!

PS: 文章中的函数名、函数作用都是我瞎编的。文章中的实现均为最简单的实现,具体项目具体需求需要具体分析,切忌直接生搬硬套!