Keeping a page alive without anybody clicking
A dashboard that refreshes, a counter going down, polling a running task. An invisible component is enough.Three parameters, and that is all
| Parameter | What it does |
|---|---|
| on_tick | what fires — a server handler, or a string evaluated in place |
| seconds | the interval, in seconds (1.0 by default) |
| active | a boolean OR a `ClientBinding` — that is what makes the cadence stoppable |
# Server: the zone re-renders every 5 s.
@refreshable(deps=[Metrics])
def board() -> None:
ui.text(f"{Metrics().running} running")
def sample() -> None:
Metrics().running = count()
board()
ui.interval(on_tick=sample, seconds=5)
Stopping it — with no task to cancel
`active=` accepts a `ClientBinding`. A switch writes into the client state, the interval reads it, and the cadence stops — with no round trip, and with no server lifecycle needing to exist.class Monitoring(ClientState):
running: bool = field(default=True)
m = Monitoring()
ui.switch(value=m.running, label="Refresh")
ui.interval(on_tick=sample, seconds=5,
active=m.running)
Without touching the server
`on_tick=` is polymorphic, like every `on_*`: a callable leaves as a signed POST, a STRING is evaluated in place. So a countdown needs no request at all.class Countdown(ClientState):
left: int = field(default=60)
c = Countdown()
ui.text(c.left)
ui.interval(on_tick=c.left.decrement(1),
seconds=1, active=c.left > 0)
`active=m.remaining > 0` is an expression of the client algebra: it becomes JavaScript, re-evaluated on every change. So the timer stops by itself at zero.What to know before using it
This is NOT a background task
The cadence lives in the PAGE. A closed tab stops running — which is exactly what one wants for a screen refresh, and exactly what one does not want for work that must finish. For that, it is `@background` — it survives the response, hence the visitor's departure.
The cost, in requests
Every tick of a server `on_tick=` is a request. A one-second interval across fifty open tabs makes fifty requests a second. When the data comes from the server and changes rarely, `@refreshable(broadcast=True)` costs less: it is the server that pushes, when it has something to say.