JavaScript 性能:Debounce 与 Throttle 的解释

29 阅读3分钟

在现代 Web 开发中,创建响应式高性能应用程序通常意味着处理触发频率极高的事件。input搜索字段、scroll长页面或resize窗口等事件每秒可能会触发数十甚至数百个函数调用。如果这些函数执行大量计算或进行 API 调用,应用程序的性能将受到极大影响。

这时,两个强大的实用函数Debounce和Throttle就派上用场了。它们有助于控制函数响应频繁事件的执行频率,从而优化应用程序并提升用户体验。

“事件垃圾邮件”问题 以搜索栏为例。如果您每次按键时都调用 API 来获取搜索结果,那么您将为单个搜索查询发送数十个请求,其中大多数请求在用户完成输入之前都是无关的。这对于客户端和服务器来说都是低效的。

scroll类似地,在每次或事件上更新复杂的 UI 元素resize可能会导致卡顿和较差的帧速率。

Debounce 和 Throttle 为这些问题提供了优雅的解决方案。

  1. 去抖动:“仅在事件停止后运行” 概念:防抖动确保函数在事件上次触发后经过指定的时间后才会执行。如果事件在延迟结束前再次触发,则计时器将被重置,函数的执行将被推迟。

可以把它想象成不断推迟会议。只有在一定时间内没有人重新安排会议的情况下,会议才会举行。

何时使用:非常适合您只对一系列快速变化后的最终结果感兴趣的事件。

搜索栏/预先输入:仅在用户暂停输入后触发 API 调用。 保存输入:用户停止输入几秒钟后自动保存表单数据。 昂贵的计算:仅当输入稳定时才执行复杂的计算。 简单实现示例:

function debounce(func, delay) { let timeout; // Stores the timer ID return function(...args) { const context = this; // Preserve 'this' context

// Clear the previous timeout if the function is called again
clearTimeout(timeout);

// Set a new timeout
timeout = setTimeout(() => {
  func.apply(context, args); // Execute the original function
}, delay);

}; }

// Example usage: function fetchSearchResults(query) { console.log(Fetching results for: "${query}"...); // In a real app, this would be an API call }

const debouncedFetch = debounce(fetchSearchResults, 500); // Wait 500ms

document.getElementById('search-input').addEventListener('input', (event) => { debouncedFetch(event.target.value); });

// HTML (for context): 在这个例子中,fetchSearchResults只有在打字暂停 500 毫秒后才会被调用。

  1. 节流:“每个时间间隔最多运行一次” 概念:节流确保函数在指定的时间窗口内最多执行一次。如果事件在该窗口期间触发多次,则后续调用将被忽略,直到窗口关闭并重置。

可以把它想象成俱乐部里的保镖。无论有多少人试图进入,每 X 秒只允许一个人进入。

何时使用:适用于触发非常频繁的事件,并且您希望确保稳定、可控的执行速率,而不是处理每个事件。

滚动事件:根据滚动位置更新 UI 元素(例如,粘性标题、进度条),但不是每个滚动的像素。 窗口调整大小:在调整大小操作期间,每秒仅重新计算几次响应式设计的布局。 拖动/移动事件:处理拖动事件以平滑更新元素的位置。 简单实现示例:

function throttle(func, limit) { let inThrottle; // Flag to indicate if we're currently "throttled" return function(...args) { const context = this;

if (!inThrottle) {
  func.apply(context, args); // Execute the original function
  inThrottle = true; // Set flag to true
  setTimeout(() => (inThrottle = false), limit); // Reset flag after delay
}

}; }

// Example usage: function updateScrollIndicators() { console.log(Scroll position: ${window.scrollY}); // Perform heavy UI updates here }

const throttledScrollHandler = throttle(updateScrollIndicators, 200); // Max once every 200ms

window.addEventListener('scroll', throttledScrollHandler); 这里,updateScrollIndicators每 200 毫秒最多会调用一次,即使用户连续滚动。

在防抖和节流之间进行选择 当您关心一阵活动之后的最终结果(例如,停止打字之后)时,可以使用去抖动功能。 当您想要限制一段时间内的执行速率时,可以使用节流阀,以确保连续事件(例如,在滚动或调整大小期间)的规律速度。 对于任何希望构建性能更高、响应更快、用户更友好的 Web 应用程序的 JavaScript 开发人员来说,掌握这两种技术都是一项宝贵的技能。

你最喜欢哪些使用去抖或节流来提升性能的实际案例?欢迎在评论区分享你的见解!作者www.mjsyxx.com