Skip to content

debounce

創建一個防抖函數,延遲執行直到停止調用一段時間。

基礎用法

typescript
import { debounce } from 'radash'

const debouncedSearch = debounce(async (query: string) => {
  const response = await fetch(`/api/search?q=${query}`)
  return response.json()
}, 300)

// 用戶輸入時,只有在停止輸入300ms後才會執行搜索
debouncedSearch('hello')
debouncedSearch('hello world')
debouncedSearch('hello world example')
// 只有最後一次調用會執行

語法

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

參數

  • fn (function): 要防抖的函數
  • delay (number): 延遲時間(毫秒)
  • options (object, 可選): 配置選項
    • immediate (boolean, 可選): 是否立即執行第一次調用,默認為false

返回值

返回一個防抖後的函數。

示例

基本防抖

typescript
import { debounce } from 'radash'

const debouncedLog = debounce((message: string) => {
  console.log('Debounced:', message)
}, 500)

// 多次快速調用
debouncedLog('hello')
debouncedLog('hello world')
debouncedLog('hello world example')
// 只有最後一次調用會在500ms後執行

搜索輸入防抖

typescript
import { debounce } from 'radash'

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

const debouncedSearch = debounce(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
  debouncedSearch(query)
})

function displayResults(results: any[]) {
  // 顯示搜索結果
  console.log('Search results:', results)
}

窗口大小調整防抖

typescript
import { debounce } from 'radash'

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

window.addEventListener('resize', debouncedResize)

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

表單提交防抖

typescript
import { debounce } from 'radash'

const debouncedSubmit = debounce(async (formData: FormData) => {
  try {
    const response = await fetch('/api/submit', {
      method: 'POST',
      body: formData
    })
    
    if (!response.ok) {
      throw new Error('Submit failed')
    }
    
    const result = await response.json()
    console.log('Form submitted successfully:', result)
  } catch (error) {
    console.error('Submit error:', error)
  }
}, 1000)

const form = document.getElementById('myForm') as HTMLFormElement
form.addEventListener('submit', (e) => {
  e.preventDefault()
  const formData = new FormData(form)
  debouncedSubmit(formData)
})

立即執行模式

typescript
import { debounce } from 'radash'

const debouncedFunction = debounce((value: string) => {
  console.log('Executed with:', value)
}, 500, { immediate: true })

// 第一次調用會立即執行
debouncedFunction('first') // 立即執行
debouncedFunction('second') // 延遲500ms後執行
debouncedFunction('third') // 延遲500ms後執行

API調用防抖

typescript
import { debounce } from 'radash'

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

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

滾動事件防抖

typescript
import { debounce } from 'radash'

const debouncedScroll = debounce(() => {
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop
  console.log('Scrolled to:', scrollTop)
  
  // 更新滾動指示器
  updateScrollIndicator(scrollTop)
}, 100)

window.addEventListener('scroll', debouncedScroll)

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

鍵盤事件防抖

typescript
import { debounce } from 'radash'

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

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

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

鼠標移動防抖

typescript
import { debounce } from 'radash'

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

document.addEventListener('mousemove', (e) => {
  debouncedMouseMove(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 { debounce } from 'radash'

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

const debouncedSearch = debounce(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 debouncedSearch(params)
  console.log('Search result:', result)
}

取消防抖函數

typescript
import { debounce } from 'radash'

const debouncedFunction = debounce((message: string) => {
  console.log('Executed:', message)
}, 1000)

// 調用函數
debouncedFunction('hello')

// 取消延遲執行
// 注意:radash的debounce不直接支持取消,但可以通過重新創建函數來實現
const cancelDebounce = () => {
  // 重新創建防抖函數來"取消"之前的調用
  const newDebouncedFunction = debounce((message: string) => {
    console.log('Executed:', message)
  }, 1000)
  
  return newDebouncedFunction
}

多個防抖函數

typescript
import { debounce } from 'radash'

// 創建多個不同延遲的防抖函數
const quickDebounce = debounce((action: string) => {
  console.log('Quick action:', action)
}, 100)

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

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

// 使用不同的防抖函數
quickDebounce('fast')
mediumDebounce('normal')
slowDebounce('slow')

條件防抖

typescript
import { debounce } from 'radash'

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

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

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

注意事項

  1. 延遲時間: 選擇合適的延遲時間,太短可能無效,太長影響用戶體驗
  2. 內存洩漏: 防抖函數會保持對原函數的引用
  3. 立即執行: 使用immediate選項可以立即執行第一次調用
  4. 異步函數: 防抖函數可以包裝異步函數
  5. 參數傳遞: 防抖函數會傳遞所有參數給原函數

與其他方法的區別

  • throttle(): 限制執行頻率,防抖是延遲執行
  • debounce(): 等待停止調用後執行
  • 手動實現: 需要更多代碼和錯誤處理

實際應用場景

  1. 搜索輸入: 防止頻繁的API調用
  2. 窗口調整: 優化布局重新計算
  3. 表單提交: 防止重復提交
  4. 滾動事件: 優化滾動處理
  5. 鍵盤事件: 減少鍵盤事件處理頻率

Released under the MIT License.