Skip to content

throttle

創建一個節流函數,限制函數的執行頻率。

基礎用法

typescript
import { throttle } from 'radash'

const throttledScroll = throttle(() => {
  console.log('Scroll event handled')
}, 100)

window.addEventListener('scroll', throttledScroll)
// 滾動時,函數最多每100ms執行一次

語法

typescript
function throttle<T extends (...args: any[]) => any>(
  fn: T,
  delay: number,
  options?: {
    leading?: boolean
    trailing?: boolean
  }
): (...args: Parameters<T>) => void

參數

  • fn (function): 要節流的函數
  • delay (number): 延遲時間(毫秒)
  • options (object, 可選): 配置選項
    • leading (boolean, 可選): 是否在開始時執行,默認為true
    • trailing (boolean, 可選): 是否在結束時執行,默認為true

返回值

返回一個節流後的函數。

示例

基本節流

typescript
import { throttle } from 'radash'

const throttledLog = throttle((message: string) => {
  console.log('Throttled:', message)
}, 1000)

// 快速連續調用
throttledLog('hello')
throttledLog('world')
throttledLog('example')
// 只有第一次調用會立即執行,後續調用會被節流

滾動事件節流

typescript
import { throttle } from 'radash'

const throttledScroll = throttle(() => {
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop
  console.log('Scroll position:', scrollTop)
  
  // 更新滾動指示器
  updateScrollIndicator(scrollTop)
}, 100)

window.addEventListener('scroll', throttledScroll)

function updateScrollIndicator(scrollTop: number) {
  const indicator = document.getElementById('scroll-indicator')
  if (indicator) {
    const percentage = (scrollTop / (document.body.scrollHeight - window.innerHeight)) * 100
    indicator.style.width = `${percentage}%`
  }
}

窗口大小調整節流

typescript
import { throttle } from 'radash'

const throttledResize = throttle(() => {
  console.log('Window resized to:', window.innerWidth, 'x', window.innerHeight)
  
  // 重新計算布局
  recalculateLayout()
}, 250)

window.addEventListener('resize', throttledResize)

function recalculateLayout() {
  // 重新計算頁面布局
  console.log('Layout recalculated')
}

鼠標移動節流

typescript
import { throttle } from 'radash'

const throttledMouseMove = throttle((x: number, y: number) => {
  console.log('Mouse position:', x, y)
  
  // 更新鼠標位置顯示
  updateMousePosition(x, y)
}, 50)

document.addEventListener('mousemove', (e) => {
  throttledMouseMove(e.clientX, e.clientY)
})

function updateMousePosition(x: number, y: number) {
  const positionElement = document.getElementById('mouse-position')
  if (positionElement) {
    positionElement.textContent = `X: ${x}, Y: ${y}`
  }
}

鍵盤事件節流

typescript
import { throttle } from 'radash'

const throttledKeyPress = throttle((key: string) => {
  console.log('Key pressed:', key)
  
  // 處理鍵盤輸入
  processKeyInput(key)
}, 200)

document.addEventListener('keydown', (e) => {
  throttledKeyPress(e.key)
})

function processKeyInput(key: string) {
  // 處理鍵盤輸入邏輯
  console.log('Processing key:', key)
}

搜索輸入節流

typescript
import { throttle } from 'radash'

const searchInput = document.getElementById('search') as HTMLInputElement

const throttledSearch = throttle(async (query: string) => {
  if (query.length < 2) return
  
  const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`)
  const results = await response.json()
  
  displayResults(results)
}, 300)

searchInput.addEventListener('input', (e) => {
  const query = (e.target as HTMLInputElement).value
  throttledSearch(query)
})

function displayResults(results: any[]) {
  console.log('Search results:', results)
}

只執行開始時的節流

typescript
import { throttle } from 'radash'

const leadingThrottle = throttle((action: string) => {
  console.log('Action:', action)
}, 1000, { leading: true, trailing: false })

// 第一次調用立即執行
leadingThrottle('start')
leadingThrottle('middle')
leadingThrottle('end')
// 只有第一次調用會執行,後續調用被忽略

只執行結束時的節流

typescript
import { throttle } from 'radash'

const trailingThrottle = throttle((action: string) => {
  console.log('Action:', action)
}, 1000, { leading: false, trailing: true })

// 第一次調用不會立即執行
trailingThrottle('start')
trailingThrottle('middle')
trailingThrottle('end')
// 只有最後一次調用會在延遲後執行

API調用節流

typescript
import { throttle } from 'radash'

const throttledApiCall = throttle(async (params: any) => {
  const response = await fetch('/api/data', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params)
  })
  
  return response.json()
}, 500)

// 在用戶操作時調用
async function handleUserAction() {
  const params = { action: 'update', data: { /* ... */ } }
  const result = await throttledApiCall(params)
  console.log('API result:', result)
}

復雜參數節流

typescript
import { throttle } from 'radash'

interface SearchParams {
  query: string
  filters: Record<string, any>
  page: number
}

const throttledSearch = throttle(async (params: SearchParams) => {
  const queryString = new URLSearchParams({
    q: params.query,
    page: params.page.toString(),
    ...params.filters
  }).toString()
  
  const response = await fetch(`/api/search?${queryString}`)
  const results = await response.json()
  
  return {
    results,
    total: results.length,
    page: params.page
  }
}, 500)

// 使用節流搜索
async function performSearch() {
  const params: SearchParams = {
    query: 'javascript',
    filters: { category: 'programming', sort: 'relevance' },
    page: 1
  }
  
  const result = await throttledSearch(params)
  console.log('Search result:', result)
}

表單驗證節流

typescript
import { throttle } from 'radash'

const emailInput = document.getElementById('email') as HTMLInputElement

const throttledValidation = throttle(async (email: string) => {
  if (!email.includes('@')) return
  
  const response = await fetch('/api/validate-email', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email })
  })
  
  const result = await response.json()
  
  if (result.valid) {
    emailInput.classList.remove('error')
    emailInput.classList.add('valid')
  } else {
    emailInput.classList.remove('valid')
    emailInput.classList.add('error')
  }
}, 300)

emailInput.addEventListener('input', (e) => {
  const email = (e.target as HTMLInputElement).value
  throttledValidation(email)
})

多個節流函數

typescript
import { throttle } from 'radash'

// 創建多個不同延遲的節流函數
const quickThrottle = throttle((action: string) => {
  console.log('Quick action:', action)
}, 100)

const mediumThrottle = throttle((action: string) => {
  console.log('Medium action:', action)
}, 500)

const slowThrottle = throttle((action: string) => {
  console.log('Slow action:', action)
}, 1000)

// 使用不同的節流函數
quickThrottle('fast')
mediumThrottle('normal')
slowThrottle('slow')

條件節流

typescript
import { throttle } from 'radash'

const conditionalThrottle = throttle((value: string, shouldExecute: boolean) => {
  if (shouldExecute) {
    console.log('Executing with value:', value)
    // 執行實際邏輯
    processValue(value)
  }
}, 300)

function processValue(value: string) {
  // 處理值的邏輯
  console.log('Processing:', value)
}

// 根據條件決定是否執行
conditionalThrottle('test', true)
conditionalThrottle('skip', false)
conditionalThrottle('execute', true)

性能監控節流

typescript
import { throttle } from 'radash'

const throttledPerformanceLog = throttle(() => {
  const memory = performance.memory
  console.log('Memory usage:', {
    used: Math.round(memory.usedJSHeapSize / 1024 / 1024) + 'MB',
    total: Math.round(memory.totalJSHeapSize / 1024 / 1024) + 'MB',
    limit: Math.round(memory.jsHeapSizeLimit / 1024 / 1024) + 'MB'
  })
}, 5000) // 每5秒最多執行一次

// 定期監控性能
setInterval(throttledPerformanceLog, 1000)

注意事項

  1. 延遲時間: 選擇合適的延遲時間,太短可能無效,太長影響響應性
  2. 內存洩漏: 節流函數會保持對原函數的引用
  3. leading/trailing: 控制函數在開始和結束時是否執行
  4. 異步函數: 節流函數可以包裝異步函數
  5. 參數傳遞: 節流函數會傳遞所有參數給原函數

與其他方法的區別

  • debounce(): 延遲執行直到停止調用,節流是限制執行頻率
  • throttle(): 限制執行頻率,確保函數按固定間隔執行
  • 手動實現: 需要更多代碼和錯誤處理

實際應用場景

  1. 滾動事件: 優化滾動處理性能
  2. 窗口調整: 限制布局重新計算頻率
  3. 鼠標移動: 減少鼠標事件處理頻率
  4. 鍵盤事件: 限制鍵盤事件處理
  5. API調用: 防止頻繁的API請求

Released under the MIT License.