Membatasi sebuah fungsi agar tidak jalan lebih sering dari sekali per jeda.
Bedanya dengan debounce sering tertukar, jadi disebutkan di sini: debounce menunggu sampai panggilannya berhenti, throttle tetap jalan sepanjang panggilannya datang — cuma dijatah. Untuk pengetikan pakai debounce; untuk scroll, resize, dan pointermove pakai throttle.
Ditulis sendiri dengan alasan yang sama seperti useDebounceFn: nol dependensi, dan timernya ikut mati bersama scope-nya.
Installation
pnpm dlx shadcn-vue@latest add @kliping/use-throttle-fn Perlu @kliping terdaftar di components.json project Anda. Lihat cara menyiapkannya.
Tanpa dependensi — tidak ada berkas lain yang ikut terpasang.
Salin kode berikut ke project Anda.
ts
import { getCurrentScope, onScopeDispose, toValue } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
export interface UseThrottleFnOptions {
/** Invoke the first call immediately. Default: true. */
leading?: boolean
/** Invoke the last call after the wait elapses. Default: true. */
trailing?: boolean
}
export function useThrottleFn<Fn extends (...args: any[]) => any>(
fn: Fn,
wait: MaybeRefOrGetter<number> = 1000,
options: UseThrottleFnOptions = {},
) {
const { leading = true, trailing = true } = options
let timer: ReturnType<typeof setTimeout> | undefined
let lastArgs: Parameters<Fn> | undefined
let lastRunAt = 0
const clearTimer = () => {
if (timer !== undefined) {
clearTimeout(timer)
timer = undefined
}
}
const cancel = () => {
clearTimer()
lastArgs = undefined
lastRunAt = 0
}
const flush = () => {
if (timer === undefined || !lastArgs) return
const pendingArgs = lastArgs
clearTimer()
lastArgs = undefined
lastRunAt = Date.now()
fn(...pendingArgs)
}
const run = (...args: Parameters<Fn>) => {
const delay = toValue(wait)
const now = Date.now()
if (lastRunAt === 0 && !leading) lastRunAt = now
const remaining = delay - (now - lastRunAt)
lastArgs = args
if (remaining <= 0 || remaining > delay) {
clearTimer()
lastRunAt = now
lastArgs = undefined
fn(...args)
return
}
if (timer === undefined && trailing) {
timer = setTimeout(() => {
timer = undefined
lastRunAt = leading ? Date.now() : 0
if (lastArgs) {
const pendingArgs = lastArgs
lastArgs = undefined
fn(...pendingArgs)
}
}, remaining)
}
}
if (getCurrentScope()) onScopeDispose(cancel)
return { run, cancel, flush }
}API
ts
export interface UseThrottleFnOptions {
/** Invoke the first call immediately. Default: true. */
leading?: boolean
/** Invoke the last call after the wait elapses. Default: true. */
trailing?: boolean
}
export function useThrottleFn<Fn extends (...args: any[]) => any>(
fn: Fn,
wait: MaybeRefOrGetter<number> = 1000,
options: UseThrottleFnOptions = {},
)