防抖函数 Debounce

前端2 min
#JavaScript
目录(3)

什么是防抖函数

debounce 防抖函数,常用于某方法短时间内多次调用,但却不需要多次响应的场景。例如搜索的时候需要调用后端接口展示数据,但是在输入阶段是逐字录入的,这时候会触发多次接口,实际上仅需要输入完后触发一次即可。或者按钮短时间内多次点击,仅需要响应一次的场景。

实现方式

防抖函数的实现思想并不复杂,下面提供了一个简单示例:

function debounce(func, wait) {
  let timer;
  return function() {
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(func, wait);
  };
};

const debounced = debounce(()=> console.log(123), 1000);
debounced()

思考

防抖函数在前端开发中经常使用,比如给事件绑定防抖函数或者绑定 DOM 事件。

<button @click="debounced"> 点击 </button>

所以我想,在服务端能不能用最小 demo,复现该效果。因此有了第一版 demo。

V1:

'use strict';
const http = require('http');

const host = '127.0.0.1';
const port = 7001;

const server = http.createServer((req, res) => {
  console.log('request received');

  function debounce(func, wait) {
    let timer;
    return function() {
      console.log('timer', !!timer);
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(func, wait);
    };
  }

  const debounced = debounce(() => console.log(123), 1000);

  debounced();

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});


server.listen(port, host, () => {
  console.log('Server running as ', host, ':', port);
});

当前版本在运行后不符合预期,多次请求,并不能共享 timer。后来更改了一版。

V2:

'use strict';
const http = require('http');

const host = '127.0.0.1';
const port = 7001;
let timer;

const server = http.createServer((req, res) => {
  console.log('request received');

  function debounce(func, wait) {
    return function() {
      console.log('timer', !!timer);
      if (timer) {
        clearTimeout(timer);
      }
      timer = setTimeout(func, wait);
    };
  }

  const debounced = debounce(() => console.log(123), 1000);

  debounced();

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});


server.listen(port, host, () => {
  console.log('Server running as ', host, ':', port);
});

timer 提成全局变量以后,确实达到了预期,但是明显与在前端中的实践略有不同。研究一番以后有了第三版。

V3:

'use strict';
const http = require('http');

const host = '127.0.0.1';
const port = 7001;

function debounce(func, wait) {
  let timer;
  return function() {
    console.log('timer', !!timer);
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(func, wait);
  };
}

const debounced = debounce(() => console.log(123), 1000);

const server = http.createServer((req, res) => {
  console.log('request received');

  debounced();

  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});


server.listen(port, host, () => {
  console.log('Server running as ', host, ':', port);
});

第三版也符合预期,但是感觉还差点意思,毕竟类似于将 func 抽到了全局。并不像前端,可以在逻辑里,封一个 debounced func 供组件和 DOM 用。

比较好奇的地方是,每次点击事件的触发和每次请求,有什么差异。为什么在请求事件中封装 func,可以共享参数。而每次请求会生成新的 func?