Open squiddle opened 11 years ago
Yes, nested/composable states are a good idea, and a frequent request. I have (vague) plans on doing a fresh 3.0 version later this summer and nested states will be high on the list for that release, but probably wont happen for a few months.
// Excuse my beginner's English Hey!
Am I right that i cannot construct following state graph?:
(e)
/ -------------- A
C ----------------| (e)
\ -------------- B
A
, B
, C
are states.
e
is event.
Pseudocode config
[
e:{from: C, to: [A, B]}
]
Or current feature available already?
Thanks!
It seems that currently setting to
as an array creates state that name is array.toString(), so in this case:
var sm = StateMachine.create({
initial:'A',
events:[
{name:'go', from: 'A', to:['B','C']},
{name:'go', from: "B,C", to:'A'}
],
callbacks:{
onchangestate: console.log.bind(console)
}
});
calling sm.go()
will cycle between "A"
and "B,C"
, interestingly in stage change callback this state is presented as an array ["B", "C"]
sm.go()
-> go A ["B", "C"]
sm.go()
-> go ["B", "C"] A
Hi, are there any updates on the possibility of composable state machines?
@colegleason - do you have an example use case that might help clarify what you need? or any thoughts on what you'd like the calling syntax to look like for composing FSM's?
@jakesgordon I can provide an example:
So I have 6 different tasks that a user can complete, and the logic of getting a task, completing a task, etc is all the same. However, the actions a user must take to complete a task are different. So I would like a task to have three states: unassigned
, assigned
, and completed
. The parent state machine would handle everything except the assigned
state, and treat that like a black box.
When the user transitions into the assigned
state, the child FSM takes over, one of the FSMs that correspond to each of the 6 specific task types. These handle all of the logic for that task, and only have to know that they kick the task into the completed
state when finished.
I've been using machina.js since I posted this, and the hierarchical state machines work pretty well, although I had to hack it to allow dynamic children FSMs that depend on the task type.
Wow, fast response! Thanks, that helps. I'm currently thinking about how to resurrect this library from its hibernation and looking to see what features might be worth adding.
@colegleason - do you have any thoughts on a composition syntax something like this:
// an example child FSM
var childFsm = StateMachine.create({
initial: 'inactive',
transitions: [
{ name: 'build', from: 'inactive', to: 'building' },
{ name: 'test', from: 'building', to: 'testing' },
{ name: 'complete', from: '*', to: 'completed' },
{ name: 'abandon', from: '*', to: 'abandoned' }
]
});
// A parent FSM using the child to manage the 'assigned' state
var fsm = StateMachine.create({
initial: 'unassigned',
composed: {
'assigned': childFsm // when entering 'assigned' state this child FSM takes over
// ... it can be an FSM or a function that returns an FSM at run-time
},
transitions: [
{ name: 'assign', from: 'unassigned', to: 'assigned' },
{ name: 'reopen', from: 'completed', to: 'unassigned' },
{ child: 'completed', from: 'assigned', to: 'completed' }, // occurs when child enters 'completed' state
{ child: 'abandoned', from: 'assigned', to: 'unassigned' } // occurs when child enters 'abandoned' state
]
});
// might be used as follows
fsm.current // "unassigned"
fsm.assign('jake')
fsm.current // "assigned"
fsm.assigned.current // "inactive"
fsm.build()
fsm.current // "assigned"
fsm.assigned.current // "building"
fsm.test()
fsm.current // "assigned"
fsm.assigned.current // "testing"
fsm.complete()
fsm.current // "completed"
fsm.assigned // nil
In this case, does the child FSM define a completed and abandoned state, or are those only defined on the parent?
On Thu, Nov 17, 2016 at 6:07 PM, Jake Gordon notifications@github.com wrote:
@colegleason https://github.com/colegleason - do you have any thoughts on a composition syntax something like this:
// an example child FSM
var childFsm = StateMachine.create({ initial: 'inactive', transitions: [ { name: 'build', from: 'inactive', to: 'building' }, { name: 'test', from: 'building', to: 'testing' }, { name: 'complete', from: '', to: 'completed' }, { name: 'abandon', from: '', to: 'abandoned' } ] });
// A parent FSM using the child to manage the 'assigned' state
var fsm = StateMachine.create({ initial: 'unassigned', composed: { 'assigned': childFsm // when entering 'assigned' state this child FSM takes over // ... it can be an FSM or a function that returns an FSM at run-time }, transitions: [ { name: 'assign', from: 'unassigned', to: 'assigned' }, { name: 'reopen', from: 'completed', to: 'unassigned' }, { child: 'completed', from: 'assigned', to: 'completed' }, // occurs when child enters 'completed' state { child: 'abandoned', from: 'assigned', to: 'unassigned' } // occurs when child enters 'abandoned' state ] });
// might be used as follows
fsm.current // "unassigned" fsm.assign('jake') fsm.current // "assigned" fsm.assigned.current // "inactive" fsm.build() fsm.current // "assigned" fsm.assigned.current // "building" fsm.test() fsm.current // "assigned" fsm.assigned.current // "testing" fsm.complete() fsm.current // "completed" fsm.assigned // nil
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/jakesgordon/javascript-state-machine/issues/47#issuecomment-261398916, or mute the thread https://github.com/notifications/unsubscribe-auth/AAOLh0O-OsLWrlNhEWnKOMJm5PP9CVXYks5q_N4-gaJpZM4An-vr .
Student, University of Illinois at Urbana-Champaign Email: cg@colegleason.com Website: colegleason.com
In this case, yes, the child defines those states - the names are arbitrary - its just a normal FSM, and the parent defines which child states should trigger a transition out of its current parent state (the 2 special transitions that have a child
attribute instead of a name
).
That keeps the child completely independent (and oblivious) of its parent.
Yeah, I wasn't thinking of that, but it makes sense.
I was thinking of the parent-child relationship more like abstract base classes: you can't use the child without the parent. However, there might be other use cases where that would be useful.
FYI: this did not make it into v3, but is still on the roadmap
I'm interested in using nested states as well. I'm thinking about incorporating a state machine into our web app to handle network connection, authentication, websocket connection, etc. It might be nice to have several layers (you need a network connection before you can be authenticated, and you need to be authenticated before you can establish a websocket connection). Then the states could start and stop additional even listening specific to the state you are in, display status on a page, etc. as the user progresses. Then, for example, if you were down in a third level nested state and your network connection dropped, you'd transition out of the top-level state (and child and grand-child states) to another top-level state (that deals with checking the connection and displaying something different to the user). Is there any possibility you might support something like this in the future?
It would be nice if a state could be represented with a state machine itself. So all events get delegated to it. Having composable state machines would be great to use in plugin-based architectures, where the plug-in can just fit into the general state machine to represent its "sub-state-machine". Right now i look into the conditional transition from #27 to solve my particular issue but i thought having composition would be nice, if others also think so.