Overview

debounce(fn, wait) delays calling fn until wait ms have passed without another call — ideal for search inputs and resize handlers. throttle(fn, limit) ensures fn fires at most once per limit ms — ideal for scroll and mousemove handlers.

Both return the wrapped function with two extra methods: .cancel() discards any pending call, and .flush() executes it immediately. They are plain utilities — not component-specific — so you can create them at module level or in a component's outer function.

debounce vs throttle

Utility
Fires when…
Best for
debounce After activity stops for wait ms Search fields, resize handlers, save-on-type — wait until the user pauses.
throttle At most once per limit ms Scroll position, mousemove, analytics — sample continuous events periodically.

debounce + throttle, plus cancel() / flush()

Move the mouse: debounce waits for idle, throttle samples. Type in the search field then hit search.cancel() to drop the pending call, or search.flush() to run it immediately.

import { html, atom, renderApp } from 'mates';
import { debounce, throttle } from 'mates';

const App = () => {
const rawCount = atom(0);
const debounceCount = atom(0);
const throttleCount = atom(0);
const lastEvent = atom('—');
const query = atom('');
const flushedQuery = atom('—');

const onDebounced = debounce(() => {
debounceCount.set(n => n + 1);
}, 400);

const onThrottled = throttle(() => {
throttleCount.set(n => n + 1);
}, 400);

// debounce returns .cancel() / .flush() on the same function
const search = debounce((q) => {
flushedQuery.set(q);
}, 500);

const handleMove = (e) => {
rawCount.set(n => n + 1);
lastEvent.set(`(${Math.round(e.clientX)}, ${Math.round(e.clientY)})`);
onDebounced();
onThrottled();
};

return () => html`
<div class="m-col m-gap">
<div
class="card card-body p-20"
@mousemove=${handleMove}