Events — the EventBus
The EventBus is Elyra's Broadcasting: Rust pushes events to the frontend,
batched per flush. Rust owns the state; the frontend subscribes to changes
instead of polling.
Emitting (Rust)
The bus is created by App and bound in the container, so any command,
provider, or task can resolve it:
#[command]
async fn tick(ctx: Ctx) {
ctx.get::<EventBus>().emit("tick", &42u32).ok();
}
emit<T: Serialize>(channel, &value) is non-blocking and callable from any
thread. To emit from main or a background thread, grab a clone before running:
let app = App::new().commands(commands![..]);
let bus = app.events(); // a clone of the bus
std::thread::spawn(move || { bus.emit("tick", &1u32).ok(); });
app.run()?;
Subscribing (frontend)
channel(name) returns a Svelte-readable store, multiplexed over a single
connection:
<script>
import { channel } from "@elyra/runtime";
const ticks = channel("tick"); // usable as $ticks
</script>
<p>{$ticks}</p>
Or subscribe manually:
const unsubscribe = channel<number>("tick").subscribe((n) => console.log(n));
Transport & batching
Events travel over a long-poll of elyra://localhost/__events: the shell
holds the request open until events are ready, responds with a MessagePack batch
([[channel, value], ...]), and the frontend immediately reconnects. Binary, no
base64, one connection for all channels. See wire format.
Emits accumulate and flush together, so N state changes cost one IPC round, not N. By default there's no artificial delay — the natural response→reconnect gap coalesces bursts. For sustained, time-spaced streams you can force frame-level coalescing:
App::new().batch_window(std::time::Duration::from_millis(8));
After ~20s idle the poll returns an empty keep-alive batch and the connection refreshes.
Fan-out: one queue per window
Every webview identifies itself with a random client id (x-elyra-client-id,
added by @elyra/runtime) and gets its own queue. An emit is fanned out to
all connected windows, so a multi-window app can't lose events — previously one
shared queue meant whichever window polled first took the batch and the others
never saw it.
Events emitted before any window has connected are held and delivered to the first poll (nothing emitted during startup is lost). A window that opens later does not get a replay of what it missed — push current state from a command instead when a new window needs to catch up.
Related
- Frontend runtime —
channel()details - System tray — tray menu clicks arrive on the
"tray"channel