Connectivity Monitor
This example monitors network connectivity using a three-state FSM. It simulates health checks client-side — no backend required — and drives a real-time UI entirely through FSM event subscriptions. The browser’s online/offline events feed the FSM as inputs; everything downstream is reactive.
What it demonstrates
Section titled “What it demonstrates”- Async operations in
_onEnter: Thecheckingstate kicks off an async health check and feeds the result back as an FSM input viafsm.handle(). The FSM stays synchronous; the async work lives in the side effect. AbortControllercleanup in_onExit: Every in-flight health check gets anAbortController. When the FSM leaveschecking,_onExitcallsabort()so stale callbacks cannot trigger unexpected transitions.- Timer management co-located in
_onEnter/_onExit: The retry timer (offline) and heartbeat timer (online) are both started in_onEnterand cleared in_onExit. Cleanup is guaranteed regardless of which input caused the transition. - Custom events emitted from handlers:
checking._onEnteremitscheckCountUpdatedso the UI can display progress without polling state.offline._onEnteremitsmaxChecksReachedwhen the retry cap is hit. - Event-driven UI:
main.tssubscribes totransitionedand custom events. The UI never reads FSM state directly — it only reacts to events.
State diagram
Section titled “State diagram”| From | Input | To |
|---|---|---|
| online | connectionLost | checking |
| offline | connectionRestored | checking |
| offline | retryCheck | checking |
| checking | healthCheckPassed | online |
| checking | healthCheckFailed | offline |
| checking | connectionLost | offline |
The online state also runs a periodic heartbeat. If it fails, it fires connectionLost — the same input the browser offline event sends. The FSM does not distinguish the source.
Key pattern
Section titled “Key pattern”The checking state is the core of the example: _onEnter kicks off async work and feeds the result back as an FSM input. _onExit aborts any in-flight request so stale callbacks can’t fire after the state is gone.
fsm is declared before createFsm() so _onEnter callbacks can close over it. This is safe because the callbacks are async — they run after createFsm() returns and fsm is assigned.