misc/throttle.js

/**
 * 函数节流 - 限制函数在指定时间间隔内最多只能执行一次
 * 
 * 节流技术常用于:
 * - 滚动事件:限制滚动监听器的执行频率
 * - 鼠标移动:减少鼠标移动事件的处理次数
 * - 窗口resize:控制窗口大小调整的响应频率
 * - API请求:防止用户频繁点击按钮导致的重复请求
 * - 游戏帧率控制:控制动画更新频率
 * 
 * @author tank
 * @category misc
 * @alias misc_throttle
 * @param {Function} fn - 需要节流的函数
 * @param {number} [interval=500] - 时间间隔(毫秒),默认 500ms
 * @returns {Function} 节流后的函数
 * @since 0.1.0
 * 
 * @example
 * // 基本用法:滚动事件节流
 * const throttledScroll = misc_throttle(() => {
 *   console.log('滚动位置:', window.scrollY);
 *   // 执行滚动相关逻辑,如懒加载、回到顶部按钮显示/隐藏等
 * }, 100); // 每 100ms 最多执行一次
 * 
 * window.addEventListener('scroll', throttledScroll);
 * 
 * @example
 * // 鼠标移动事件节流
 * const throttledMouseMove = misc_throttle((event) => {
 *   console.log('鼠标位置:', event.clientX, event.clientY);
 *   // 更新鼠标跟随效果、拖拽操作等
 * }, 50); // 每 50ms 最多执行一次,保证流畅性
 * 
 * document.addEventListener('mousemove', throttledMouseMove);
 * 
 * @example
 * // 窗口大小调整节流
 * const throttledResize = misc_throttle(() => {
 *   console.log('窗口大小:', window.innerWidth, window.innerHeight);
 *   // 重新计算布局、更新图表尺寸等
 * }, 250); // 每 250ms 最多执行一次
 * 
 * window.addEventListener('resize', throttledResize);
 * 
 * @example
 * // 按钮点击节流:防止用户快速重复点击
 * const saveButton = document.getElementById('save');
 * const throttledSave = misc_throttle(() => {
 *   console.log('正在保存...');
 *   // 执行保存逻辑
 *   saveData().then(() => {
 *     console.log('保存成功');
 *   });
 * }, 2000); // 2 秒内最多执行一次
 * 
 * saveButton.addEventListener('click', throttledSave);
 * 
 * @example
 * // API 请求节流
 * const throttledSearch = misc_throttle(async (query) => {
 *   if (!query.trim()) return;
 *   
 *   console.log('搜索:', query);
 *   try {
 *     const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
 *     const results = await response.json();
 *     displaySearchResults(results);
 *   } catch (error) {
 *     console.error('搜索失败:', error);
 *   }
 * }, 1000); // 每秒最多请求一次
 * 
 * searchInput.addEventListener('input', (e) => {
 *   throttledSearch(e.target.value);
 * });
 * 
 * @example
 * // 在游戏或动画中使用
 * const throttledGameUpdate = misc_throttle(() => {
 *   // 更新游戏状态、渲染画面等
 *   updateGameState();
 *   renderGame();
 * }, 16); // 约60FPS,每 16.67ms 最多执行一次
 * 
 * function gameLoop() {
 *   throttledGameUpdate();
 *   requestAnimationFrame(gameLoop);
 * }
 * gameLoop();
 * 
 * @example
 * // 在 React/Vue 等框架中使用
 * import { misc_throttle } from 'jxk';
 * 
 * export default {
 *   data() {
 *     return {
 *       scrollPosition: 0,
 *       isScrolling: false
 *     };
 *   },
 *   mounted() {
 *     this.throttledScrollHandler = misc_throttle(this.handleScroll, 100);
 *     window.addEventListener('scroll', this.throttledScrollHandler);
 *   },
 *   beforeDestroy() {
 *     window.removeEventListener('scroll', this.throttledScrollHandler);
 *   },
 *   methods: {
 *     handleScroll() {
 *       this.scrollPosition = window.scrollY;
 *       this.isScrolling = true;
 *       
 *       // 滚动停止检测
 *       clearTimeout(this.scrollTimer);
 *       this.scrollTimer = setTimeout(() => {
 *         this.isScrolling = false;
 *       }, 150);
 *     }
 *   }
 * };
 * 
 * @note 节流函数会立即执行第一次调用,然后在指定时间间隔内忽略后续调用
 * @note 与防抖(debounce)不同,节流保证函数在指定时间间隔内至少执行一次
 * @note 节流函数不会返回值,如果需要返回值,请使用回调函数或 Promise
 */
export default (fn, interval) => {
  let inThrottle;
  interval = interval || 500;
  return function () {
    const args = arguments;
    const that = this;
    if (!inThrottle) {
      fn.apply(that, args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), interval);
    }
  };
};