-
Notifications
You must be signed in to change notification settings - Fork 1
Transitions
Transition is the way to change state of State Machine.
In general, Transition consists of:
- Trigger, type of Event that activates Transition,
- Source, a State that transition is coming from,
- Target, a State that transition is coming to,
- Guard, logic that decides if Transition should happen,
- Effect, logic that is run when Transition happens.
There are three types of Transitions:
- 'Regular' Transition that moves from one State to another,
- Internal Transition that has no Target State,
- Default Transition that has no explicit Trigger Event.
Transitions are always defined on the level of Source State.
Single State can have multiple Transitions triggered by various Event types. Moreover, there can be multiple Transitions triggered by the same Event type.
UML notation of Transition:
stateDiagram-v2
Source --> Target : Trigger [Guard] / Effect
Equivalent Stateflows notation of Transition:
/* fragment of State Machine definition */
.AddState("Source", b => b
.AddTransition<Trigger>("Target", b => b
.AddGuard(async c => true)
.AddEffect(async c => {})
)
)
.AddState("Target")
/* fragment of State Machine definition */
.AddState<Source>(b => b
.AddTransition<Trigger, TriggerTransition, Target>()
)
.AddState<Target>()
Using lambda style means that logic parts within Transition (Guard, Effect) are implemented directly in State Machine as lambdas. In typed style these logic parts are implemented in separate class.
There are two places where logic can be injected into Transition of any type, both optional:
-
Guard:
- determines if Transition should happen,
- must return bool value,
- lack of Guard on Transition means that there is an implicit true-returning Guard,
- should not cause any side effect,
- can be called multiple times on multiple Transitions, even the ones which won't eventually happen.
-
Effect:
- is called when Transition happens, after OnExit of Source State and before OnEntry of Target State,
- can cause side effects.
The way of adding logic to Transition varies depending on used declaration style. For more details, refer to Guards or Effects, respectively.
When Event is sent to State Machine, Stateflows finds all Transitions triggered with this Event's type in current State's stack. Guards of these Transitions are then evaluated, one by one, and first Transition which Guard returns true
is fired. That causes Source State to exit, Transition Effect is run, then Target State is entered.
Note that Internal Transition doesn't cause Source State to exit nor Target State to enter.
stateDiagram-v2
Source : Source<br/>/onExit
Source --> Target1 : Event1 [Guard1] / Effect2
Source --> Target2 : Event1 [Guard2] / Effect2
Target1 : Target1<br/>/onEntry
Target2 : Target2<br/>/onEntry
Let's consider above example:
- Current State of a State Machine is
Source
, - Event
Event1
is sent to a State Machine, -
Guard1
is evaluated, returnsfalse
, -
Guard2
is evaluated, returnstrue
, -
Source
'sonExit
is called, -
Effect2
is called, -
Target2
'sonEntry
is called, - Execution is complete with
Target2
as new current State of a State Machine.
When State contains multiple Transitions with various Guards, sometimes it is handy to use Else Transition - a Guard-less Transition which is fired when all other Transitions with the same Trigger fail to fire. Such Transition can be considered a fallback from all of these Transitions.
stateDiagram-v2
Source : Source<br/>/onExit
Source --> Target1 : Event1 [Guard1] / Effect2
Source --> Target2 : Event1 [Guard2] / Effect2
Source --> Target3 : Event1 [else] / Effect3
Target1 : Target1<br/>/onEntry
Target2 : Target2<br/>/onEntry
Target3 : Target3<br/>/onEntry
Let's consider above example:
- Current State of a State Machine is
Source
, - Event
Event1
is sent to a State Machine, -
Guard1
is evaluated, returnsfalse
, -
Guard2
is evaluated, returnsfalse
, -
Source
'sonExit
is called, -
Effect3
is called, -
Target3
'sonEntry
is called, - Execution is complete with
Target3
as new current State of a State Machine.
Else-ness of Transition is based on a Trigger type, not Transition type!
Transitions defined on a higher nesting level of States structure are evaluated before more lower nesting level Transitions.
stateDiagram-v2
state State1 {
Substate1 : Substate1<br/>/onExit
Substate2 : Substate2<br/>/onEntry
Substate1 --> Substate2 : Event1 / Effect1
}
State1 --> State2 : Event1
Let's consider above example:
- Current States of a State Machine are
State1
/Substate1
, - Event
Event1
is sent to a State Machine, - Both available Transitions are Triggered by
Event1
, but inner Transition has precedence over outer Transition, -
Substate1
'sonExit
is called, -
Effect1
is called, -
Substate2
'sonEntry
is called, - Execution is complete with
State1
/Substate2
as new current States of a State Machine.