Documentation Index
Fetch the complete documentation index at: https://abxbus.archivebox.io/llms.txt
Use this file to discover all available pages before exploring further.
Debouncing is most useful when events trigger expensive work:
- screenshots or browser automation
- external API calls
- LLM/tool runs
- heavyweight DB/file operations
Instead of starting duplicate work every time, reuse:
- a recent matching event (
past window), or
- a matching event that is about to be emitted by another caller (
future wait), or
- both (history-first, then short future wait, then emit).
Debouncing in AbxBus is built from 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.)
See Find Events for full option semantics.
Pattern 1: Reuse recent completed work (history-only)
Use when “fresh enough” cached results are acceptable.
Python
TypeScript
Rust
Go
existing = await bus.find(
ScreenshotEvent,
where=lambda e: e.url == url,
past=10, # look back 10s
future=False, # do not wait
)
event = existing or bus.emit(ScreenshotEvent(url=url))
await event.now()
result = await event.event_result()
const existing = await bus.find(
ScreenshotEvent,
(event) => event.url === url,
{ past: 10, future: false }
)
const event = existing ?? bus.emit(ScreenshotEvent({ url }))
await event.now()
const result = await event.eventResult()
use abxbus_rust::{
event,
event_bus::FindOptions,
};
use futures::executor::block_on;
use serde_json::json;
use std::collections::HashMap;
event! {
struct ScreenshotEvent {
url: String,
event_result_type: serde_json::Value,
}
}
let existing = block_on(bus.find_with_options(
ScreenshotEvent::event_type,
FindOptions {
past_window: Some(10.0),
future: None,
where_filter: Some(HashMap::from([("url".to_string(), json!(url))])),
..FindOptions::default()
},
));
let event = existing.unwrap_or_else(|| {
bus.emit(ScreenshotEvent { url: url.to_string(), ..Default::default() })
});
block_on(event.now());
let result = block_on(event.event_result())?;
existing, err := bus.Find("ScreenshotEvent", func(event *abxbus.BaseEvent) bool {
return event.Payload["url"] == url
}, &abxbus.FindOptions{
Past: 10.0, // look back 10s
Future: false, // do not wait
})
if err != nil {
panic(err)
}
event := existing
if event == nil {
event = bus.Emit(abxbus.NewBaseEvent("ScreenshotEvent", map[string]any{"url": url}))
}
if _, err := event.Now(); err != nil {
panic(err)
}
result, err := event.EventResult()
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
Rust
Go
in_flight = await bus.find(
ScreenshotEvent,
where=lambda e: e.url == url,
past=False, # skip history
future=2, # wait up to 2s for another caller to emit
)
event = in_flight or bus.emit(ScreenshotEvent(url=url))
await event.now()
result = await event.event_result()
const inFlight = await bus.find(
ScreenshotEvent,
(event) => event.url === url,
{ past: false, future: 2 }
)
const event = inFlight ?? bus.emit(ScreenshotEvent({ url }))
await event.now()
const result = await event.eventResult()
let in_flight = block_on(bus.find_with_options(
ScreenshotEvent::event_type,
FindOptions {
past: false,
future: Some(2.0),
where_filter: Some(HashMap::from([("url".to_string(), json!(url))])),
..FindOptions::default()
},
));
let event = in_flight.unwrap_or_else(|| {
bus.emit(ScreenshotEvent { url: url.to_string(), ..Default::default() })
});
block_on(event.now());
let result = block_on(event.event_result())?;
inFlight, err := bus.Find("ScreenshotEvent", func(event *abxbus.BaseEvent) bool {
return event.Payload["url"] == url
}, &abxbus.FindOptions{
Past: false, // skip history
Future: 2.0, // wait up to 2s for another caller to emit
})
if err != nil {
panic(err)
}
event := inFlight
if event == nil {
event = bus.Emit(abxbus.NewBaseEvent("ScreenshotEvent", map[string]any{"url": url}))
}
if _, err := event.Now(); err != nil {
panic(err)
}
result, err := event.EventResult()
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
Rust
Go
event = (
await bus.find(ScreenshotEvent, where=lambda e: e.url == url, past=10, future=False)
or await bus.find(ScreenshotEvent, where=lambda e: e.url == url, past=False, future=2)
or bus.emit(ScreenshotEvent(url=url))
)
await event.now()
result = await event.event_result()
const event =
(await bus.find(ScreenshotEvent, (e) => e.url === url, { past: 10, future: false })) ??
(await bus.find(ScreenshotEvent, (e) => e.url === url, { past: false, future: 2 })) ??
bus.emit(ScreenshotEvent({ url }))
await event.now()
const result = await event.eventResult()
let by_url = HashMap::from([("url".to_string(), json!(url))]);
let event = block_on(bus.find_with_options(
ScreenshotEvent::event_type,
FindOptions {
past_window: Some(10.0),
future: None,
where_filter: Some(by_url.clone()),
..FindOptions::default()
},
))
.or_else(|| {
block_on(bus.find_with_options(
ScreenshotEvent::event_type,
FindOptions {
past: false,
future: Some(2.0),
where_filter: Some(by_url.clone()),
..FindOptions::default()
},
))
})
.unwrap_or_else(|| {
bus.emit(ScreenshotEvent { url: url.to_string(), ..Default::default() })
});
block_on(event.now());
let result = block_on(event.event_result())?;
sameURL := func(event *abxbus.BaseEvent) bool {
return event.Payload["url"] == url
}
event, err := bus.Find("ScreenshotEvent", sameURL, &abxbus.FindOptions{Past: 10.0, Future: false})
if err != nil {
panic(err)
}
if event == nil {
event, err = bus.Find("ScreenshotEvent", sameURL, &abxbus.FindOptions{Past: false, Future: 2.0})
if err != nil {
panic(err)
}
}
if event == nil {
event = bus.Emit(abxbus.NewBaseEvent("ScreenshotEvent", map[string]any{"url": url}))
}
if _, err := event.Now(); err != nil {
panic(err)
}
result, err := event.EventResult()
Pattern 4: Keyed helper for repeated use
Wrap the debounce logic once and reuse it for all expensive keyed actions.
Python
TypeScript
Rust
Go
async def emit_debounced_screenshot(url: str):
event = (
await bus.find(ScreenshotEvent, where=lambda e: e.url == url, past=15, future=False)
or await bus.find(ScreenshotEvent, where=lambda e: e.url == url, past=False, future=3)
or bus.emit(ScreenshotEvent(url=url))
)
await event.now()
return await event.event_result()
const emitDebouncedScreenshot = async (url: string) => {
const event =
(await bus.find(ScreenshotEvent, (e) => e.url === url, { past: 15, future: false })) ??
(await bus.find(ScreenshotEvent, (e) => e.url === url, { past: false, future: 3 })) ??
bus.emit(ScreenshotEvent({ url }))
await event.now()
return await event.eventResult()
}
fn emit_debounced_screenshot(bus: &EventBus, url: &str) -> Result<Option<serde_json::Value>, String> {
let by_url = HashMap::from([("url".to_string(), json!(url))]);
let event = block_on(bus.find_with_options(
ScreenshotEvent::event_type,
FindOptions {
past_window: Some(15.0),
future: None,
where_filter: Some(by_url.clone()),
..FindOptions::default()
},
))
.or_else(|| {
block_on(bus.find_with_options(
ScreenshotEvent::event_type,
FindOptions {
past: false,
future: Some(3.0),
where_filter: Some(by_url),
..FindOptions::default()
},
))
})
.unwrap_or_else(|| {
bus.emit(ScreenshotEvent { url: url.to_string(), ..Default::default() })
});
block_on(event.now());
block_on(event.event_result())
}
func emitDebouncedScreenshot(ctx context.Context, bus *abxbus.EventBus, url string) (any, error) {
sameURL := func(event *abxbus.BaseEvent) bool {
return event.Payload["url"] == url
}
event, err := bus.Find("ScreenshotEvent", sameURL, &abxbus.FindOptions{Past: 15.0, Future: false})
if err != nil {
return nil, err
}
if event == nil {
event, err = bus.Find("ScreenshotEvent", sameURL, &abxbus.FindOptions{Past: false, Future: 3.0})
if err != nil {
return nil, err
}
}
if event == nil {
event = bus.Emit(abxbus.NewBaseEvent("ScreenshotEvent", map[string]any{"url": url}))
}
if _, err := event.Now(); err != nil {
return nil, err
}
return event.EventResult()
}
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.now(), then await event.event_result()
- TypeScript:
await event.now(), then await event.eventResult()
- Go:
event.Now(), then event.EventResult()
- 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
past window can become less effective.
See also: