jakesgordon / javascript-state-machine Goto Github PK
View Code? Open in Web Editor NEWA javascript finite state machine library
License: MIT License
A javascript finite state machine library
License: MIT License
Is there a way to get the valid events the current state can take?
Looking at the declaration of fsm.can
it looks like it would just be finding all the events that include the current state in from
?
My use case is getting all the valid events and creating a button for each valid event.
Any thoughts on debugging TypeError: Object # has no method 'apply'?
I'm building the FSM inside a class as follows:
this.fsm = StateMachine.create({
initial: constants.S_CSM_COLD,
events: [
{ from: constants.S_CSM_COLD, name: constants.E_SCK_GUI_STARTUP, to: constants.S_CSM_REGISTER},
{ from: constants.S_CSM_REGISTER, name: constants.E_CSM_REGISTER_SUCCESS, to: constants.S_CSM_WAITING},
....
],
callbacks: {
onS_CSM_REGISTER: this.register,
...
}
});
Inside a callback from a socket.io connect, I call:
self.fsm.E_SCK_GUI_STARTUP();
and it transitions into my register state.
However, from within my register state, I'm trying to fire the next transition by calling:
self.fsm.E_CSM_REGISTER_SUCCESS();
However, it is generating the TypeError: Object # has no method 'apply' error.
Not sure how the first transitions fine, but the second doesn't.
Any help greatly appreciated.
Hi,
First of all, let me say thanks for this state machine project. It's rocking my world!
Unfortunately, I'm dealing with what I think is a bug.
I am using async transitions, and doing some basic validation during each state's 'onleave' callback. If the validation fails, I return false.
For example
onleaveState1 = function (event, from, to, args) {
// Check for required param
args = args || {};
// if the required param isn't there, return false to
// cancel the transition
if (!args.requiredParam) {
return false;
}
// If the required param is there, run
// an async function to do awesome stuff
awesomeAsyncFunction(args);
return StateMachine.ASYNC;
I am getting strange behaviour where a previously-cancelled transition, cancelled by 'return false', throws an error when it is called a second time. The error message I get is:
uncaught exception: event myTransition inappropriate because previous transition did not complete
I can reproduce the bug every time by doing these steps:
The error doesn't crash the script, and everything works as normal, but the error is still being thrown and I can't solve it.
EDIT: This bug is now sporadically crashing my script. I have also tried to fix it by explicitly cancelling the transition, with transition.cancel(), instead of returning false. This doesn't seem to work either.
We at VersionEye are working hard to keep up the quality of the bower's registry.
We just finished our initial analysis of the quality of the Bower.io registry:
7530 - registered packages, 224 of them doesnt exists anymore;
We analysed 7306 existing packages and 1070 of them don't have bower.json on the master branch ( that's where a Bower client pulls a data ).
Sadly, your library jakesgordon/javascript-state-machine
is one of them.
Can you spare 15 minutes to help us to make Bower better?
Just add a new file bower.json
and change attributes.
{
"name": "jakesgordon/javascript-state-machine",
"version": "1.0.0",
"main": "path/to/main.css",
"description": "please add it",
"license": "Eclipse",
"ignore": [
".jshintrc",
"**/*.txt"
],
"dependencies": {
"<dependency_name>": "<semantic_version>",
"<dependency_name>": "<Local_folder>",
"<dependency_name>": "<package>"
},
"devDependencies": {
"<test-framework-name>": "<version>"
}
}
Read more about bower.json on the official spefication and nodejs semver library has great examples of proper versioning.
NB! Please validate your bower.json with jsonlint before commiting your updates.
Thank you!
Timo,
twitter: @versioneye
email: [email protected]
VersionEye - no more legacy software!
Any chance to register the library to Bower?
It is quite trivial to work directly with the git repository but having it listed there saves one browser visit here. Thanks! :)
Any thoughts on supporting nondeterministic and epsilon transitions? I.e. nondeterministic transitions would enable that a machine transitions from one state to multiple states for a single event, while epsilon transitions would enable transitions between states without reading an event (which is usually modeled using a special 'epsilon' event).
Not sure what your goals for the library are, but having these two features would make it a full FSM library for a wide specter of use-cases.
Thanks for a such great framework.
It has been my goal to use a statechart on the frontend together with ExtJS and I finally found a good one.
However I have some questions about the design choice of this framework.
Having used Sproutore's statechart I wonder why each event by default is triggering a transitioning from one state to another? If I am in a let's say "loggedin" state I should be able to "createpost", "deletepost", "searchposts" etc without having to enter a new state. Maybe I only want a transition to the "loggedout" state on "logout" event.
Or am I missing something?
It's helpful if the event methods return true or false based on whether the event was successful or was cancelled.
In my use case, I want to keep statistics on event firings. This could also be used for situations where an event can unpredictably fail and multiple retires is necessary until it succeeds.
So that tests could iterate over most or all states without having to have a separately maintained list from that in the code itself.
I made a StackOverflow question to get better coverage. Is this an appropriate place to ask questions?
It seems an onafterevent isn't being fired, and I'm not sure if it's a bug. I posted my question here.
Is there an obvious error in the way I set up my events? Thanks in advance.
The new transitions()
method does not work for events that have multiple from states. e.g
var fsm = StateMachine.create({
events: [
{ name: 'start', from: 'none', to: 'green' },
{ name: 'warn', from: ['green','red'], to: 'yellow' },
{ name: 'panic', from: ['green','yellow'], to: 'red' },
{ name: 'clear', from: ['red','yellow'], to: 'green' },
]});
onenter is triggered before onchangestate, we need something that is called after everything is done/all callbacks are triggered,
because we want to change state after entering a state (state is entered via n transitions -> we want to take care of callbacks in this state)
I'm trying this in IE8 and get an error
'from' is null or not an object
state-machine.js Line 38 Char 9
Code: 0
It's possible to use this to get a ready setup IE8 to test in ..
https://github.com/xdissent/ievms
Line 77 in the callback has a try/catch which swallows errors in SM transition callbacks. This is quite annoying in development for debugging as it hides exactly what is going wrong. It would be great if you could either remove it or bubble the error message up.
I have some (preliminary) plans on building a new 3.0 version, almost certainly NOT backward compatible, but including some of the common requests for:
Currently certain things will aways trigger an exception, and there is nothing that can be done about it short of wrapping calls to the state machine in a try/catch block.
I think it would be desirable to allow a special callback to be triggered instead of throwing an exception, in some circumstances.
they are good for logging and debugging and so bracketing specific events would be useful
I'm not sure how this fits in with the state machine programming paradigm but external observers attached to states on the state machine I think would be a really good feature.
sm = new StateMachine() ...
observer = sm.observe('stateName', function() { alert('in the state'); });
sm.doStateChange() would fire the observer.
observer would contain a UID which could be used to unbind the observer again:
sm.removeObserver(observer);
Thoughts? Would this be a good fit for the project or is it straying from the core functionality too far? Maybe I'm missing something with what's there already :)
Hello,
It would be nice if we could define final state(s) for the machine, so we could query it like fsm.isFinished() or something.
This could be declared by adding a new property to the constructing object called 'final', similar to the 'initial' one.
Greetings!
It seems that your license requires attribution, but your source code does not contain any attribution comment. I think you need to add an attribution or else the files cannot be used according to your license. Or do you expect each person to manually create a new version of the file to add the license attribution themselves?
In fact, I can think of more use cases where the departing state doesn't let the event to complete because of some unfinished business, as opposed to the actual event itself. Right now, onbeforeevent is the only callback from which returning false would result in cancellation of the event. I propose the same functionality in onleavestate.
Currently, returning false from onleavestate triggers an async transition. This could be changed to a different value (e.g. "async" string). It's only after we have left the old state that it no longer makes sense to cancel the event. Up until then, both callbacks should have this ability.
Here's a use case:
I would love to be able to define events which are only allowed to fire from within a state, but which don't trigger a state transition. I believe this is an integral part of the statechart pattern: for example, if there are things that should only happen when my app is in "account management" state or something... this would be an ideal way to manage that. That code could run secure in the knowledge that it's running from the correct state.
events: [
{ name: 'intrastate-event', from: 'myState' }
]
callbacks: {
'onintrastate-event': function() { console.log('run some code that can assume it's being run from myState. :D'); }
}
This great lib, as is, cannot be used in a web worker.
Fortunately, the fix is trivial. I'll send a PR.
How would you recommend adding support for promises (jquery.Deferred or Q.js) to the async/transition handler?
My understanding is that entering and leaving a state triggers the onenter[state]
and onleave[state]
callbacks, which is true.
I would have also expected that events always fire their onbefore[event]
and onafter[event]
callbacks, even if the event is taking the state machine from its current state to that same state. After all, the event is occurring, is it not?
If the code in the event-related callback is not supposed to run if the state isn't changing, there are three ways to solve this problem:
But if the event-related callback's code should be running on transitions from one state to the same state… then you have only one solution, and it sucks:
There are obvious implied downsides, not the least of which is code bloat and the forced constraints on the on[enter/leave][state] callbacks for states which have faux transition-to-self support.
Because I would expect events to operate differently than they do, I consider the results here a bug:
http://jsfiddle.net/alanhogan/5Z6Hd/1/
I would expect the output to read:
Entering Y
State machine is: y
(We will now call sm.dox().)
Event: dox
Leaving Y
Entering X
(We will now call sm.dox().)
Event: dox
But instead it reads:
Entering Y
State machine is: y
(We will now call sm.dox().)
Event: dox
Leaving Y
Entering X
(We will now call sm.dox().)
When I get an error like "an exception occurred in a caller-provided callback function" is there a way of finding out what that exception might be? If not, this is a huge problem for debugging.
I could be nice to support error when using can with unexpected even:
var fsm = StateMachine.create({
initial: 'hungry',
events: [
{ name: 'eat', from: 'hungry', to: 'satisfied' },
{ name: 'eat', from: 'satisfied', to: 'full' },
{ name: 'eat', from: 'full', to: 'sick' },
{ name: 'rest', from: ['hungry', 'satisfied', 'full', 'sick'], to: 'hungry' },
]})
fsm.can('sleep');
produces the following error:
.can = function(event) { return !this.transition && (map[event].hasOwnProp
^
TypeError: Cannot call method 'hasOwnProperty' of undefined
It may be possible to return false
instead of getting an error?
I'm writting a parser using state machine mechanism, so all my transitions depend on the input source that I cannot control that why I can stay in this use case :)
I will want to create a work flow something like this
Root
|
Start
/ \
Node0 Node1
\ /
Node2 Node3
\ /
Node4
Is this possible with this API. If yes how can thanks
Would be convenient. For example I have events that are coming from two different sources, user touch events and a backend so I'd love to pass those namespaces to those two sources to use without exposing the other interface.
For example:
var fsm = new StateMachine({
events: [
{ name: "backend.foo", from: "s1", to: "s2 },
{ name: "backend.bar", from: "s1", to: "s2 },
{ name: "frontend.babe", from: "s2", to: "s3 },
{ name: "frontend.pig", from: "s3", to: "s4" }
]
});
var backend = new Backend({ interface: fsm.backend });
var frontend = new FrontEnd({ interface: fsm.frontend });
And in Backend, for example:
interface.foo()
Hi Jake,
I thought this library was better than most FSM out there and so I ported it to python.
https://github.com/oxplot/fysom
Cheers,
Mansour
In Sproutcore you can have a state with child states and so on.
Eg. I can have "loggedin" state with child states like "adminpanel", "buypanel" etc.
Any thoughts about including this in the future?
Depending on the system, it might be desirable to have conditional branching logic for the transitions. I've implemented this feature via an optional function parameter for each event definition. If you are interested let me know and I'll clean it up a bit and send the pull request.
It's possible to pause the statemachine by returning StateMachine.ASYNC from the onleave event. The point I'm trying to make, is that method 1 shouldn't know anything about method 2.
So, if method 2 is running some async code and the statemachine has to wait while it's running, than method 2 should be responsible for pausing the statemachine instead of (an event of) method 1
Idea's to solve the problem:
The first can be used like:
callbacks: {
onbeforeFetch: function(event, from, to) {
return StateMachine.ASYNC;
},
onFetch: function(event, from, to) {
$.getJSON(url, function(data) {
this.transition();
});
}
}
and the second like: (my personal favorite)
callbacks: {
onFetch: function(event, from, to) {
this.transition.await()
$.getJSON(url, function(data) {
this.transition();
}).error(function() {
this.transition.cancel();
});
}
}
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.
(from twitter) @rentzsch says: @jakesgordon I'm loving jsm, thanks! One thing: I'm having to if(!fsm.is('foo')) fsm.foo() a bunch. Consider making it not an error?
Absolutely, will do, but with some caveats...
The issue sounds like, in 140 chars :-), you are triggering an event from a state that is not allowed, and in this case you are already in that state ? The state-machine is defensive by default, in most cases triggering an event from an innapropriate state would be a logic error.
If you want the event to be allowed and just a NO-OP (instead of an error), you can add the appropriate from
state to the declaration. Example, a traffic light that has
{ name: 'red', from: 'yellow', to: 'red' },
would become
{ name: 'red', from: ['yellow', 'red'], to: 'red' },
or, an alternate, but identical declaration is
{ name: 'red', from: 'yellow', to: 'red' },
{ name: 'red', from: 'red', to: 'red' }, // NO-OP: gracefully ignore red => red transitions
Both formats, would allow the red()
event to be called from either the yellow
or the red
state and end up in the red
state.
I could probably add something like an ignore_noop_transition: true
setting to enable this behavior for the entire instance, so that if you find yourself needing this for every event you can just set this one setting.
Thoughts ?
As a sidenote from this issue, I highly recommend keeping event names separate from state names, even though it is often tempting to use the same name it becomes confusing. So for example the
eat
event might transition to theeating
state... or the traffic light eventturnRed
might transition from theyellow
state to thered
state.
Great library!
What do you think about the create
method being able to add methods to a constructor function and then expose a much simpler/faster instance creation method from that? It would be useful in the cases where an application has to manage many state machines.
Hi there,
When you return a StateMachine.ASYNC value from an event callback, the expectation is that you are later going to call my-state-machine.transition() to go to the new state. However, I can't see a way to cancel that transition if there was an error somewhere in my asynchronous callback.
Looking at the code, what I did was take my-state-machine.transition member, which is a function, and set it to false. I did this just by looking at the algorithm in the source code. But there should definitely be a function call to cancel the transition, after an StateMachine.ASYNC call. This is, unless, I've missed it in the documentation.
Thanks
Currently, it is possible to define an event from and to the same state. However, firing that event will not call any callbacks.
Logically, we are not leaving the current state nor entering another one, so calling state callbacks (onleavestate, onenterstate) is meaningless. However, we are firing an event and expect something to happen as result of it. I think it makes sense to fire onbeforeevent and onafterevent (or just the latter) in this situation.
My use case:
I have a SM with couple of states, each of which maintain an internal SM. Events on the top level SM can trigger events internal to each state. Hence, there could be events firing on a single state of the top level SM which only affects SM internal to that state. If the original event doesn't call and callbacks, there would be no way to trigger anything else.
support amd?
I saw you mentioned this as a future feature but I'm bringing it up again so it is listed as a feature request in issues and you know people are interested.
Thanks for this great little project.
Code example under "Asynchronous State Transitions" reads:
onleavegame: function() {
$('#game').slideDown('slow', function() {
Surely it should be:
onleavegame: function() {
$('#game').slideUp('slow', function() {
since slideDown shows the element instead of hiding it.
I was just wondering if you intend to tag version 2.2.0 if it is considered officially released? If not, is version 2.1.0 considered the last stable version?
Thanks!
Hi,
More of a query than an issue as I'm really loving the work you've done so far. It's really cool to have a version of the FSM that's ready to be brought in by requireJS. What's stumping me is how you might add the FSM to an existing module after you've brought it in as a dependency? From what I've tried you can't simply target the prototype in this instance. Any ideas? I'm happy to fork off and try some of my own stuff ... just wondered if I was missing something or if it's something you're currently looking at.
Thanks,
J
Is it possible to define an event foo
that takes machine from state a
to state b
, if we are in state a
, or from state x
to state y
, if we are in state x
?
This would be useful for constructing state machines that track to variables, say i
and j
, and we want to move variable j
from j1
to j2
regardless of what state i
is in.
Does that make sense?
Saving a document. We want to track whether the document is dirty since last save, and whether a save is in progress. Marking the document as dirty should change state, but we don't want to forget whether a save is in progress.
Event dirty
should:
clean
to dirty
, orcleanAndSaving
to dirtyAndSaving
Is this possible?
According to the documentation, a new instance should have the following member:
fsm.transitions() - return list of events that are allowed from the current state
This doesn't appear to be implemented?
I like to have my state machine objects encapsulated as classes so that I can say
var myFSM = new MyFSM();and I can immediately go on and use it.
This works fine as long as I do not specify the 'initial' option in the 'StateMachine.create' command as in this example:
var Foo = function() { this.counter = 0; this.createStateMachine(); }; Foo.prototype.getCount = function() { return this.counter; }; Foo.prototype.onchangestate = function(event, from, to) { console.log("Foo:[" + from + "]---" + event + "-->[" + to + "]"); }; Foo.prototype.onenterready = function() { console.log('onenterready:' + this.counter); this.counter++; }; Foo.prototype.onenterrunning = function() { console.log('onenterrunning:' + this.counter); this.counter++; }; Foo.prototype.createStateMachine = function() { StateMachine.create({ target : Foo.prototype, events : [{name: 'execute', from: 'ready', to: 'running'}, {name: 'abort', from: 'running', to: 'ready'}], initial : 'ready'}); }; var foo = new Foo(); assertTrue(foo.is('ready')); foo.execute(); assertEquals(2, foo.getCount());
The output I get is
[LOG] onenterready:undefined [LOG] Foo:[none]---startup-->[ready] [LOG] onenterrunning:0 [LOG] Foo:[ready]---execute-->[running] AssertError: expected 2 but was 1
I.e. the enter state handler of the initial state is called but it has no access to the this properties of the constructor.
Maybe this is perfectly intended by the library, however I was unable to find another class concept to integrate the 'StateMachine.create' factory method. And I would really like to do it this way.
I this behaviour intended or is it a bug?
Best regards
Semmel
I will want to create a work flow something like this
Root
|
Start
/ \
Node0 Node1
\ /
Node2 Node3
\ /
Node4
Is this possible with this API. If yes how can thanks
Hi,
I've got a problem with the way the state machine is handling the errors happening in "caller-provided".
I spent an hour finding out a bug as the error displayed was: "an exception occurred in a caller-provided callback function", displayed from a try-catch that is part of the library:
javascript-state-machine/state-machine.js
Line 95 in fcdd0fa
I understand the attempt in trying to give more information about the states when something wrong is happening but the problem is that it is really masking completely the real error.
I tried to log the error from the try catch in the state machine but all I got was a useless: TypeError {}
with no information at all. Not sure why I couldn't propagate the real error on my side. I don't have a try-catch at that level in my code but we are using a large tv application framework that might have some. Anyway, that's out of my reach.
I found the issue in my code by removing the try-catch from the state-machine and calling the callback directly, all finally got the error description, in which javascript file, at what line.
Conceptually, I do not think that the state machine should have a try-catch there for several reasons:
The only things the state machine should do is have a a condition that checks that the function is actually a function, and that's it:
if (typeof func === 'function') {
return func.apply(fsm, [name, from, to].concat(args));
}
Would you consider a pull request to remove the try-catch? We are re-building a large TV application and I need it rock solid. I'd prefer to contribute to the original library rather than using my own fork.
Cheers.
Romu
When trying to call an event directly from setTimeout was getting an exception that the method cannot was not defined on an object. Traced it down to the fact that buildEvent uses 'this', which when used in a method called by setTimeout will be the root object (window in case of a browser).
Some example code:
var fsm = StateMachine.create({
initial: 'green',
events: [
{ name: 'warn', from: 'green', to: 'yellow' },
{ name: 'panic', from: 'yellow', to: 'red' },
{ name: 'calm', from: 'red', to: 'yellow' },
{ name: 'clear', from: 'yellow', to: 'green' }
],
callbacks: {
onwarn: function () {
alert("Made it!");
}
}
});
setTimeout(fsm.warn, 0);
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.