- screenshots or browser automation
- external API calls
- LLM/tool runs
- heavyweight DB/file operations
- a recent matching event (
pastwindow), or - a matching event that is about to be emitted by another caller (
futurewait), or - both (history-first, then short future wait, then emit).
find(...) + conditional emit(...).
Debounce building blocks
past: search recent history (true/false/seconds)future: optionally wait for a matching future emit (true/false/seconds)where/ event-field filters: scope matching to the same “work key” (url, account_id, document_id, etc.)
Pattern 1: Reuse recent completed work (history-only)
Use when “fresh enough” cached results are acceptable.- Python
- TypeScript
Pattern 2: Coalesce concurrent callers (future-only)
Use when many callers may request the same expensive action at the same time. Caller A emits first. Caller B waits briefly for that same event instead of emitting a duplicate.- Python
- TypeScript
Pattern 3: Hybrid debounce (past + short future + emit)
This is the most practical default for expensive endpoints.- Reuse recent match.
- If none, wait briefly for someone else to emit.
- If still none, emit new work.
- Python
- TypeScript
Pattern 4: Keyed helper for repeated use
Wrap the debounce logic once and reuse it for all expensive keyed actions.- Python
- TypeScript
Important behavior notes
find(...)resolves when an event is emitted, not when handlers finish.- Always await completion after selecting a debounced event:
- Python:
await event, thenawait event.event_result() - TypeScript:
await event.done(), thenevent.event_result
- Python:
- Debouncing scope depends on your match key (
where/ event-field filters).
Use the narrowest key that represents “same work.” - Debouncing depends on retained history. If history is aggressively trimmed, your
pastwindow can become less effective.