Throttle / Debounce
Per-topic publish rate limiting. Wraps platform.publish() to coalesce rapid-fire updates (mouse position, typing indicators, live metrics). Sends the latest value at most once per interval. No timers to manage yourself.
Two modes:
throttle(ms)- sends immediately on first call (leading edge), then at most once per interval (trailing edge). Latest value wins within each interval.debounce(ms)- waits until no calls for the full interval, then sends the latest value. Each new call resets the timer.
Setup
import { throttle, debounce } from 'svelte-adapter-uws/plugins/throttle';
const mouse = throttle(50); // at most once per 50ms per topic
const search = debounce(300); // wait for 300ms of silence Usage
// In hooks.ws.js
import { mouse, search } from '$lib/server/rate-limiters';
export function message(ws, { data, platform }) {
const msg = JSON.parse(Buffer.from(data).toString());
if (msg.type === 'cursor') {
// 60 mouse moves/sec from 20 users = 1200 publishes/sec
// With throttle(50), each topic publishes at most 20/sec
mouse.publish(platform, 'cursors', 'move', {
userId: ws.getUserData().id,
x: msg.x, y: msg.y
});
}
if (msg.type === 'search') {
// User types fast -- only publish when they pause
search.publish(platform, 'search-results', 'query', { q: msg.q });
}
} Rate limiting is per-topic. If you call mouse.publish() for topics 'room-a' and 'room-b', each topic has its own independent timer.
API
| Method | Description |
|---|---|
limiter.publish(platform, topic, event, data) | Publish with rate limiting |
limiter.flush() | Send all pending immediately, clear all timers |
limiter.flush(topic) | Send pending for one topic |
limiter.cancel() | Discard all pending, clear all timers |
limiter.cancel(topic) | Discard pending for one topic |
limiter.interval | The configured interval in ms |
How throttle works
t=0 publish({x:0}) --> sends immediately (leading edge)
t=10 publish({x:1}) --> stored (latest)
t=30 publish({x:2}) --> stored (overwrites x:1)
t=50 [timer fires] --> sends {x:2} (trailing edge)
t=60 publish({x:3}) --> stored
t=100 [timer fires] --> sends {x:3}
t=150 [timer fires] --> nothing pending, goes idle
t=200 publish({x:4}) --> sends immediately (new leading edge) How debounce works
t=0 publish({q:"h"}) --> stored, timer starts
t=80 publish({q:"he"}) --> stored, timer resets
t=160 publish({q:"hel"}) --> stored, timer resets
t=260 [timer fires, 100ms] --> sends {q:"hel"} Limitations
- Server-side only. No client component - the client receives messages at the throttled rate naturally.
- Latest value only. Intermediate values within an interval are discarded, not queued. If you need every message delivered, don’t throttle.
- Timer-based. Uses
setTimeoutinternally. Precision depends on Node.js event loop load (typically under 1ms drift).
Was this page helpful?