"An event is a public address system. A channel is a private phone line opened for one call."
When One Command Has Many Things to Say
A command returns once. But some work produces a stream of updates: a file download reporting bytes, a long job emitting progress, a generator yielding tokens. You could broadcast events, but then every listener everywhere hears them and you have to correlate which update belongs to which call. Tauri's Channel<T> solves this cleanly — it's a typed, ordered stream scoped to a single invoke.
How It Wires Up
The frontend creates a Channel, sets its onmessage handler, and passes it as a command argument. The Rust command receives a Channel<T> parameter and calls .send(payload) as often as it likes; each send fires the frontend handler in order. When the command returns, the stream for that call is done. No event-name collisions, no correlation IDs — the channel is the correlation.
Channel vs Event vs External Stream
Pick by scope. Event: global, anyone can listen, good for app-wide news. Channel: request-scoped, one caller, good for progress of this operation. And when the stream comes from a separate process entirely — not your Tauri core — neither applies; you reach for a real network transport. Cinder streams generated images from a separate brain process over a WebSocket, precisely because the producer isn't inside the Tauri core. Same 'streaming' word, three different right answers depending on where the data is born.