Deferred Input
Sometimes an input arrives when the FSM isn’t ready to handle it. Rather than dropping it or making the caller retry, call defer() inside a handler to queue the input for automatic replay after a future transition. The caller gets a clean return; the FSM sorts it out when it’s in the right state.
Targeted defer
Section titled “Targeted defer”Pass { until: "stateName" } to specify which state should replay the input. The input stays queued until the FSM enters that state, then replays automatically — regardless of how many intermediate transitions occur in between.
The deferred input survives any number of intermediate transitions — it only replays when the target state is entered.
Untargeted defer
Section titled “Untargeted defer”Call defer() with no arguments to replay the input on the very next transition to any state. This is useful in error or recovery states where you don’t know the destination ahead of time.
When the FSM is in error, any unrecognized input hits the "*" catch-all and gets queued. When retry fires and the FSM transitions to idle, all queued inputs replay there.
Replay mechanics
Section titled “Replay mechanics”A few things worth knowing before you rely on this in production code:
- FIFO order. Deferred inputs replay in the order they were queued.
- Replay happens after
_onEntercompletes on the target state. The state is fully initialized before any queued input lands. - Re-deferring is allowed. If a replayed input calls
defer()again, it goes back in the queue. No infinite loops — it just waits for the next appropriate state. - Cascading transitions. If a replayed input triggers a transition, the remaining deferred inputs pause and wait for the next appropriate state entry before continuing to replay.
With BehavioralFsm
Section titled “With BehavioralFsm”Deferred inputs are tracked per-client, not globally. Calling defer() inside a BehavioralFsm handler queues the input for that specific client only — other clients are unaffected.
Real-world examples
Section titled “Real-world examples”- Shopping Cart — targeted defer across multiple transitions (
recordPurchaseAnalyticsqueued frombrowsingthroughvalidatingandapplyingDiscountall the way tocheckout), plus untargeted defer in theerrorstate’s"*"catch-all. - Traffic Intersection — pedestrian button press deferred from
greentointerruptibleGreen; if the button is pressed too early in the green phase, it queues and automatically shortens the interruptible portion when that window opens.