鲁斯前端布鲁斯前端

文章中英模式

布鲁斯前端JS面试题目 - 实现节流函数优化数据请求

学习如何实现节流函数,控制高频事件触发频率,优化滚动加载、频繁API请求等前端性能问题。

影片縮圖

懒得看文章?那就来看视频吧

什么是节流 (Throttle)?

节流是一种优化技术,用于限制函数在一段时间内的执行频率。与防抖(Debounce)仅执行最后一次操作不同,节流确保函数在指定的时间间隔内最多执行一次。

核心原理:固定间隔时间内只执行一次函数,无论事件触发频率多高,效果类似于水龙头的流量控制。

节流应用场景

  • 1. 滚动事件处理(如滚动加载、固定导航栏)
  • 2. 监听resize事件调整布局
  • 3. 拖拽操作更新界面
  • 4. 频繁的数据请求或API调用
  • 5. 实时游戏中的按键响应
  • 6. Canvas 绘图操作

基本实现:节流函数 (Throttle)

以下是两种常见的实现方式:时间戳方法和定时器方法。

方法一:时间戳实现

function throttle(func, delay = 300) {
  let lastTime = 0;
  
  return function(...args) {
    const now = Date.now();
    
    // 检查距离上次执行是否已经超过延迟时间
    if (now - lastTime >= delay) {
      func.apply(this, args);
      lastTime = now;
    }
  };
}

方法二:定时器实现

function throttle(func, delay = 300) {
  let timer = null;
  
  return function(...args) {
    // 如果没有定时器运行中,则设置新的定时器
    if (!timer) {
      timer = setTimeout(() => {
        func.apply(this, args);
        timer = null;
      }, delay);
    }
  };
}

方法三:综合方法(首尾调用都不丢失)

function throttle(func, delay = 300) {
  let timer = null;
  let lastTime = 0;
  
  return function(...args) {
    const now = Date.now();
    const remaining = delay - (now - lastTime);
    
    // 清除定时器并立即执行
    if (remaining <= 0) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      
      func.apply(this, args);
      lastTime = now;
    } else if (!timer) {
      // 设置定时器,确保最后一次操作被执行
      timer = setTimeout(() => {
        func.apply(this, args);
        lastTime = Date.now();
        timer = null;
      }, remaining);
    }
  };
}

实际应用:节流优化数据请求

以下是一些节流函数在数据请求中的实际应用:

案例一:滚动加载数据

// 页面滚动时加载更多数据
function loadMoreData() {
  console.log('加载更多数据...');
  
  // 计算滚动位置
  const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
  const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
  const clientHeight = document.documentElement.clientHeight || window.innerHeight;
  
  // 滚动到底部附近时,加载更多
  if (scrollTop + clientHeight >= scrollHeight - 100) {
    fetch('/api/posts?page=' + currentPage)
      .then(response => response.json())
      .then(data => {
        // 处理数据,添加到页面上
        displayData(data);
        currentPage++;
      });
  }
}

// 使用节流函数优化滚动事件
const throttledLoadMore = throttle(loadMoreData, 500);

window.addEventListener('scroll', throttledLoadMore);

案例二:即时搜索建议

// 发送搜索请求
function fetchSearchResults(query) {
  console.log('搜索:', query);
  
  fetch(`/api/search?q=${encodeURIComponent(query)}`)
    .then(response => response.json())
    .then(results => {
      // 更新搜索结果显示
      updateSearchResults(results);
    })
    .catch(error => {
      console.error('搜索错误:', error);
    });
}

// 使用节流函数限制请求频率
const throttledSearch = throttle(fetchSearchResults, 300);

// 绑定到输入事件
document.querySelector('#search-input').addEventListener('input', (e) => {
  const query = e.target.value.trim();
  if (query.length > 2) {
    throttledSearch(query);
  }
});

节流与防抖比较

节流和防抖都是限制事件处理频率的方法,但适用于不同场景:

特性节流 (Throttle)防抖 (Debounce)
执行时机固定时间内最多执行一次等待指定时间后执行,期间若再触发则重新计时
执行频率稳定固定的频率仅在事件停止后执行一次
适用场景滚动加载、拖拽效果、频繁API调用搜索建议、表单验证、窗口调整大小
类比水龙头限流电梯等待所有人进入后再启动