【力扣】2627. 函数防抖

【力扣】2627. 函数防抖

文章目录

  • [【力扣】2627. 函数防抖](#【力扣】2627. 函数防抖)

一、题目

请你编写一个函数,接收参数为另一个函数和一个以毫秒为单位的时间 t ,并返回该函数的 函数防抖 后的结果。

函数防抖 方法是一个函数,它的执行被延迟了 t 毫秒,如果在这个时间窗口内再次调用它,它的执行将被取消。你编写的防抖函数也应该接收传递的参数。

例如,假设 t = 50ms ,函数分别在 30ms60ms100ms 时调用。前两个函数调用将被取消,第三个函数调用将在 150ms 执行。如果改为 t = 35ms ,则第一个调用将被取消,第二个调用将在 95ms 执行,第三个调用将在 135ms 执行。

上图展示了了防抖函数是如何转换事件的。其中,每个矩形表示 100ms,反弹时间为 400ms。每种颜色代表一组不同的输入。

请在不使用 lodash 的 _.debounce() 函数的前提下解决该问题。

示例 1:

复制代码
输入:
t = 50
calls = [
  {"t": 50, inputs: [1]},
  {"t": 75, inputs: [2]}
]
输出:[{"t": 125, inputs: [2]}]
解释:
let start = Date.now();
function log(...inputs) { 
  console.log([Date.now() - start, inputs ])
}
const dlog = debounce(log, 50);
setTimeout(() => dlog(1), 50);
setTimeout(() => dlog(2), 75);

第一次调用被第二次调用取消,因为第二次调用发生在 100ms 之前
第二次调用延迟 50ms,在 125ms 执行。输入为 (2)。

示例 2:

复制代码
输入:
t = 20
calls = [
  {"t": 50, inputs: [1]},
  {"t": 100, inputs: [2]}
]
输出:[{"t": 70, inputs: [1]}, {"t": 120, inputs: [2]}]
解释:
第一次调用延迟到 70ms。输入为 (1)。
第二次调用延迟到 120ms。输入为 (2)。

示例 3:

复制代码
输入:
t = 150
calls = [
  {"t": 50, inputs: [1, 2]},
  {"t": 300, inputs: [3, 4]},
  {"t": 300, inputs: [5, 6]}
]
输出:[{"t": 200, inputs: [1,2]}, {"t": 450, inputs: [5, 6]}]
解释:
第一次调用延迟了 150ms,运行时间为 200ms。输入为 (1, 2)。
第二次调用被第三次调用取消
第三次调用延迟了 150ms,运行时间为 450ms。输入为 (5, 6)。

提示:

  • 0 <= t <= 1000
  • 1 <= calls.length <= 10
  • 0 <= calls[i].t <= 1000
  • 0 <= calls[i].inputs.length <= 10

二、解决方案

概述

这个问题要求你实现 防抖(debounce) 高阶函数。在调用防抖函数后,提供的函数应该在一定延迟 t 后被调用。然而,如果在 t 毫秒内再次调用防抖函数,提供的函数的执行应该被取消并重置计时器。

为了具体说明防抖的工作原理,让我们看一个例子:

复制代码
const start = Date.now();

function log() {
  console.log(Date.now() - start);
}

setTimeout(log, 10); // 输出:10
setTimeout(log, 20); // 输出:20
setTimeout(log, 50); // 输出:50
setTimeout(log, 60); // 输出:60

正如预期的那样,log 函数会按照 setTimeout 指定的延迟被调用。

但是,如果我们对log函数使用防抖:

复制代码
const start = Date.now();

function log() {
  console.log(Date.now() - start);
}

const debouncedLog = debounce(log, 20);

setTimeout(debouncedLog, 10); // 被取消
setTimeout(debouncedLog, 20); // 输出:40
setTimeout(debouncedLog, 50); // 被取消
setTimeout(debouncedLog, 60); // 输出:80

在上面的示例中,t=10ms 时的函数调用被取消,因为在 t=20ms 时的调用发生在 20ms 内。而t=20ms 时的调用被延迟了 20ms。

同样,t=50ms 处的函数调用被取消,因为 t=60ms 处的调用在 20ms 内发生。t=60ms 处的调用延迟了 20ms。

防抖的应用场景

主要的应用场景是当你不希望某些用户交互的结果立刻影响用户体验时。

一个经典的防抖应用场景是用户在搜索框中输入文本。如果每次键入字符时都立即显示新的结果,重新渲染新的筛选列表的时间可能会超过输入另一个字符所需的时间。这将导致用户在键入时出现令人沮丧的延迟(这是你可能经历过的情况)。

理想情况下,渲染筛选列表的函数应该被防抖。这样,它只在用户输入完成后才进行渲染。

另一个例子是通过捏合缩放来放大图表。在放大后,需要下载更多数据。但如果在用户缩小时以每秒 60 帧的速度进行新请求,会非常低效。就像在输入示例中一样,应该在用户完成缩放后才查询更多数据。

复制代码
async function fetchNewData(startDate, endDate) {
  // 请求逻辑
}
const debouncedFetch = debounce(fetchNewData, 300);

chart.on('zoom', (startDate, endDate) => {
  debouncedFetch(startDate, endDate);
});

Lodash 库有一个流行的防抖实现。

方法 1:setTimeout + clearTimeout

每次调用防抖函数时,我们希望在一定延迟后执行 fn。我们可以使用setTimeout来实现这个目标。但我们还需要保留返回的 timeout 的引用,以便在函数再次调用时取消fn的执行(通过 clearTimeout)。需要注意的是,在 undefined 或已完成的 timeout 上调用 clearTimeout 将不会有任何效果。

JavaScript 复制代码
var debounce = function(fn, t) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      fn(...args)
    }, t);
  };
};

方法 2:setInterval + clearInterval

setInterval 的工作方式类似于 setTimeout,但它将以一定的时间间隔不断执行回调,直到 clearInterval 被调用。

我们通过将间隔设置为非常小的值,例如 1ms,来模拟 setTimeout 的行为。然后,通过不断检查是否 Date.now() - lastCall >= t 来检查是否已经经过了适当的时间。一旦满足这个条件,我们应该调用fn并停止间隔。每次调用函数时,我们也应该中止任何现有的间隔,以取消fn的挂起执行。

需要注意的是,这个解决方案仅用于演示目的,因为它非常低效。

JavaScript 复制代码
var debounce = function(fn, t) {
  let interval;
  return function(...args) {
    const lastCall = Date.now()
    clearInterval(interval);
    interval = setInterval(() => {
      if (Date.now() - lastCall >= t) {
        fn(...args);
        clearInterval(interval);
      }
    }, 1);
  }
};
相关推荐
এ慕ོ冬℘゜30 分钟前
前端实战:jQuery实现动态表格渲染与状态切换功能
前端·javascript·jquery
zzzzzz31034 分钟前
react-bits:把酷炫动效从“灵感截图”变成可复用的 React 组件
javascript·react.js·动效
jay神1 小时前
【计算机毕业设计】基于Flask+Vue的电影数据可视化与智能推荐系统
前端·vue.js·python·信息可视化·flask·毕业设计·课程设计
FreeTinker7 小时前
HTML本地存储技术解析:localStorage与sessionStorage的原理、差异与应用场景
前端·html
qq_452396238 小时前
第十二篇:《全链路监控与可观测性:前端错误追踪与性能分析》
前端
裕晟资质规划8 小时前
武器装备科研生产单位保密资质申请方法论:条件模型与流程拆解
人工智能·算法
海兰8 小时前
【应用】基于 Next.js 16 + Python mplfinance的金融K线图与技术指标可视化平台(三)
javascript·python·金融
wangruofeng8 小时前
Phosphor Icons 官网源码拆解:URL 即存储、水波动画与 7362 Star 图标库的架构实践
前端·架构·github
BigTopOne9 小时前
WebRTC Native / Android 开发学习资源整理
前端
en.en..10 小时前
C语言 标准输入 / 输出缓冲区
算法