Creates a new instance with only one registered state - Exception.
When extending the class, you should register your states by using either the registerAll or register methods at the end of every sub constructor.
Target object for the transitions, useful when composing a machine.
Automatically registers all defined states. Works only in case there no other attributes defined on the prototype.
List of current ticks per state.
Promise created by one of the delayed mutation methods:
...ByCallback
...ByListener
Resolved once the callback/listener gets called.
If true, this machine is currently during a transition.
If true, this machine's queue is currently being executed.
List of handlers receiving log messeges
TODO docs TODO rename TPipeBindings to TPipeBinding TODO copy these to once() and emit()
TODO docs TODO extract eventToStateName(name: string): (TStates | States) and loose the type casting
Map of piped states and their targets.
Queue execution got postponed, because this machine is currently during a transition from another machine's queue.
If true, an exception will be printed immediately after it's thrown. Automatically turned on with logLevel > 0.
List of active states.
List of all registered state names
Target object for the transitions, useful when composing the states instance.
Currently executing transition (if any)
All exceptions are caught into this state, including both synchronous and asynchronous ones, coming from async methods and callbacks. You can override it with your own and handle exceptions based on passed state data and the actual state.
The exception object.
Target states of the transition during which the exception was thrown.
Base states in which the transition originated.
The handler state which thrown the exception.
Only for delayed mutations like addByCallback, these are states which we're supposed to be activated by the callback.
Example of a custom exception handler
states = machine(['A', 'B', 'C'])
states.Exception_state = function(err, target_states) {
// Re-adds state 'C' in case of an exception when A is active.
if (exception_states.some((state) => state == 'C') && this.is('A')) {
states.add 'C'
}
}
Example of a manually thrown exception
states.A_state = function(states) {
foo = new SomeAsyncTask()
foo.start()
foo.once('error', (error) => {
this.add('Exception', error, states)
}
}
Adds specified states to the currently active ones.
OPTIONAL. Pass it if you want to execute a transition on an external machine, but using this machine's queue.
Array of state names or a single state name.
Params to be passed to the transition handlers (only the ones belonging to the explicitly requested states, not implied or auto states).
Result of the transition. false
can mean that either the
requested states weren't accepted or that the transition has been aborted
by one of the negotiation handlers. null
means that the machine is busy
and the mutation has been queued.
Basic usage
const example = machine(['A', 'B', 'C'])
example.add 'A'
example.is() // -> ['A']
example.add('B')
example.is() // -> ['B']
State negotiation
const example = machine(['A', 'B'])
// reject the transition with a negotiation handler
example.A_enter = function() {
return false
}
example.add('A' ) // -> false
Adding a state on an external machine
const m1 = machine(['A', 'B'])
const m2 = machine(['C', 'D'])
m1.A_enter = function() {
// this transition will be queued and executed after the current
// transition is completed
m1.add(m2, 'C') // -> null
}
Deferred version of add, returning a node-style callback for adding the state. Errors are handled automatically and forwarded to the Exception state.
After the call, the related promise object is available as the last_promise attribute.
See add for the params description.
Example
const example = machine(['A', 'B', 'C'])
someNodeCallback('foo.com', example.addByCallback('B'))
Deferred version of add, returning a listener for adding the state.
Errors need to be handled manually by binding the exception state to the 'error' event (or equivalent).
After the call, the related promise object is available as the last_promise attribute.
See add for the params description.
Example
const example = machine(['A', 'B', 'C'])
emitter = new EventEmitter
emitter.on('ready', example.addByListener('A'))
emitter.on('error', example.addByListener('Exception'))
Deferred version of add, adding the requested states on the next event loop's tick. Useful if you want to start with a fresh stack trace.
See add for the params description.
Example
const example = machine(['A', 'B', 'C'])
example.add('A')
example.addNext('B')
example.is() // -> ['A', 'B']
Checks if any of the passed states is active. State can also be an array, then all states from this param has to be active.
State names and/or lists of state names.
states = machine(['A', 'B', 'C'])
states.add(['A', 'B'])
states.any('A', 'C') // -> true
states.any(['A', 'C'], 'C') // -> false
Override for EventEmitter method calling a specific listener. Binds to a promis if returned by the listener.
TODO incorporate into EE, saving one call stack frame
Binds the Exception state to the promise error handler. Handy when working with promises.
See Exception_state.
The promise to handle
States for which the promise was created (the one that failed).
The source promise, for piping.
Returns the current tick of the passed state.
State's clock starts with 0 and on each activation gets incremented by 1. Ticks lets you keep control flow's integrity across async listeners, by aborting them once the state changes. Easiest way to get the tick-based abort function is to use getAbort.
Name of the state
Current tick of the passed state
Example
const example = machine(['A', 'B', 'C'])
example.add('A')
example.add('A')
example.clock('A') // -> 1
example.drop('A')
example.add('A')
example.clock('A') // -> 2
`
Creates a prototype child with it's own active states, clock, queue and locks.
Useful for creating new instances of machines created using the machine factory in an efficient manner.
Example
const parent = machine(['A', 'B', 'C'])
const child = parent.createChild()
child.add('A')
child.is() // -> ['A']
parent.is() // -> []
`
// TODO write a test
Diffs two state sets and returns the ones present only in the first one.
List of source states.
List of states to diff against (picking up the non existing ones).
List of states in states1, but not in states2.
De-activates specified states if any of them is currently active.
OPTIONAL. Pass it if you want to execute a transition on an external machine, but using this machine's queue.
Array of state names or a single state name.
Params to be passed to the transition handlers (only the ones belonging to the explicitly requested states, not implied or auto states).
Result of the transition. false
can mean that either the
requested states weren't accepted or that the transition has been aborted
by one of the negotiation handlers. null
means that the machine is busy
and the mutation has been queued.
Basic usage
const example = machine(['A', 'B', 'C'])
states.drop('A')
states.is() // -> ['A']
states.drop('B')
states.is() // -> ['B']
State negotiation
const example = machine(['A', 'B'])
// reject the transition with a negotiation handler
states.A_enter = function() {
return false
}
states.add('A') // -> false
Dropping a state on an external machine
const m1 = machine(['A', 'B'])
const m2 = machine(['C', 'D'])
m1.A_enter = function() {
// this transition will be queued and executed after the current
// transition is completed
m1.drop(m2, 'C') // -> null
}
Deferred version of drop, returning a node-style callback for dropping the state. Errors are handled automatically and forwarded to the Exception state.
After the call, the related promise object is available as the last_promise attribute.
See drop for the params description.
Example
const example = machine(['A', 'B', 'C'])
someNodeCallback('foo.com', states.dropByCallback('B'))
Deferred version of drop, returning a listener for dropping the state.
Errors need to be handled manually by binding the exception state to the 'error' event (or equivalent).
After the call, the related promise object is available as the last_promise attribute.
See drop for the params description.
Example
const example = machine(['A', 'B', 'C'])
emitter = new EventEmitter
emitter.on('ready', example.dropByListener('A'))
emitter.on('error', example.setByListener('Exception'))
Deferred version of drop, dropping the requested states on the next event loop's tick. Useful if you want to start with a fresh stack trace.
See drop for the params description.
Example
const example = machine(['A', 'B', 'C'])
states.add('A')
states.dropNext('A')
states.is('A') // -> true
Indicates if this instance is currently during a transition.
When a machine is during a transition, all state changes will be queued and executed once the transition and the previously queued state changes are finished. See queue.
Example
const example = machine(['A', 'B', 'C'])
example.A_enter = function() {
this.duringTransition() // -> true
}
example.A_state = function() {
this.duringTransition() // -> true
}
example.add('A')
`
Returns the list of active states from which the current transition started.
Requires [[duringTranstion]] to be true or it'll throw.
Returns state's properties and relations.
State name.
states = machine(['A', 'B', 'C'])
states.A = { drop: ['B'] }
states.get('A') // -> { drop: ['B'] }
Returns an abort function, based on the current clock tick of the passed state. Optionally allows to next an existing abort function.
The abort function return a boolean true
in case the flow for the
specific state should be aborted, because:
true
Example
const example = machine(['A', 'B', 'C'])
example.A_state = function() {
const abort = this.getAbort('A')
setTimeout( () => {
if (abort()) return
console.log('never reached')
}, 0)
}
example.add('A')
example.drop('A')
`
TODO support multiple states
Name of the state
Existing abort function (optional)
A new abort function
Returns an object for a given handler.
Handler's name
Returns defined relations between two registered states.
List of relations.
Return true
if the current state differs from the states_before
.
List of previously active states.
Managed the ID of a machine.
Sets or gets and also support returning a normalized version.
Example
const example = machine()
// set
example.id('a b c')
// get
example.id() // -> 'a b c'
// get normalized
example.id(true) // -> 'a-b-c'
Without any params passed, returns all of the current states.
When a list of states is provided, returns a boolean if all of them are currently active.
If only one state is passed, you can assert on a certain tick of that state (see clock).
One or more state names.
When checking only one state, additionally asserts the clock value for that state.
states = machine(['A', 'B'])
states.add('A')
states.is('A') // -> true
states.is(['A']) // -> true
states.is(['A', 'B']) // -> false
// assert the tick
tick = states.clock('A')
states.drop('A')
states.add('A')
states.is('A', tick) // -> false
Return a list of assigned event listeners.
Push a new message to the log with an optional level (defaults to 1).
Enables debug messages sent to the console (or the custom handler).
There's 4 log levels:
Example
const example = machine(['A', 'B', 'C'])
example.logLevel(1)
example.add('A')
// -> [add] state Enabled
// -> [states] +Enabled
`
Returns true
in case of all of the passed states aren't active.
Example:
const example = machine(['A', 'B', 'C', 'D'])
example.add(['A', 'B'])
// not(A) and not(C)
example.not(['A', 'C']) // -> false
// not(C) and not(D)
example.not(['C', 'D']) // -> true
Parse state names, check if they exist and always return an array. Throws in of an error.
State names to check
An array of valid state names.
Pipes (forwards) a state to another machine.
Name of the state to pipe.
Target machine to which the state should be forwarded to.
If the target state name should be different, this is that name. Applicable if only one state is being piped.
Different modes of piping. See PipeFlags.
Piping without negotiation
const m1 = machine(['A', 'B', 'C'])
const m2 = machine(['A', 'B', 'C'])
m1.pipe('A', m2)
m1.add('A')
m2.is('A') // -> true
Piping with negotiation
import { PipeFlags } from 'asyncmachine'
const m1 = machine(['A', 'B', 'C'])
const m2 = machine(['A', 'B', 'C'])
m2.A_enter = function() {
return false
}
m1.pipe('A', m2, null, PipeFlags.NEGOTIATION)
m1.add('A')
m2.is('A') // -> false
Pipes all the states from this machine to the target
. Doesn't pipe the
Exception
state.
The exception state is never piped.
Target machine to which the state should be forwarded.
Removes an existing pipe. All params are optional.
Source states. Empty means any state.
Target machine. Empty means any machine.
Pipe flags. Empty means any flags.
TODO optimise, if needed
TODO should remove a binding returned by pipe() and pipeAll() methods
Register passed state names. State properties should be already defined.
State names.
const example = machine()
example.Enabled = {}
example.Disposed = { drop: ['Enabled'] }
states.register('Enabled', 'Disposed')
states.add('Enabled')
states.is() // -> 'Enabled'
Registers all defined states. Use it only if you don't define any other attributes on the object (or it's prototype). If you do, register the states manually with the register method. There's also a shorthand for this method as AsyncMachine.constructor's param.
class States extends AsyncMachine {
constructor() {
this.A = {}
this.B = {}
this.registerAll()
console.log(this.states_all) // -> ['Exception', 'A', 'B']
}
}
Remove all listeners or only the listeners for the specified event.
Remove event listeners.
Activates only the specified states and de-activates all the other ones which are currently active.
OPTIONAL. Pass it if you want to execute a transition on an external machine, but using this machine's queue.
Array of state names or a single state name.
Params to be passed to the transition handlers (only the ones belonging to the explicitly requested states, not implied or auto states).
Result of the transition. false
can mean that either the
requested states weren't accepted or that the transition has been aborted
by one of the negotiation handlers. null
means that the machine is busy
and the mutation has been queued.
Basic usage
const example = machine(['A', 'B', 'C'])
example.set('A') // -> true
example.is() // -> ['A']
example.set('B') // -> true
example.is() // -> ['B']
State negotiation
const example = machine(['A', 'B'])
// reject the transition with a negotiation handler
example.A_enter = function() {
return false
}
example.add('A') // -> false
Activating a state on an external machine
const m1 = machine(['A', 'B'])
const m2 = machine(['C', 'D'])
m1.A_enter = function() {
// this transition will be queued and executed after the current
// transition is completed
m1.add(m2, 'C') // -> null
}
Sets the new active states incrementing the counters.
An array of previously active states.
Deferred version of set, returning a node-style callback for setting the state. Errors are handled automatically and forwarded to the Exception state.
After the call, the related promise object is available as the last_promise attribute.
See set for the params description.
Example
const example = machine(['A', 'B', 'C'])
setTimeout(example.setByCallback('B'))
Deferred version of set, returning a listener for setting the state.
Errors need to be handled manually by binding the exception state to the 'error' event (or equivalent).
After the call, the related promise object is available as the last_promise attribute.
See set for the params description.
Example
const example = machine(['A', 'B', 'C'])
emitter = new EventEmitter
emitter.on('ready', states.setByListener('A'))
emitter.on('error', states.addByListener('Exception'))
Deferred version of set, setting the requested states on the next event loop's tick. Useful if you want to start with a fresh stack trace.
See set for the params description.
Example
states = machine(['A', 'B', 'C'])
states.set('A')
states.setNext('B')
states.is() // -> ['A']
Sets the target for the transition handlers. Useful to keep all your methods in in one class while the states class is composed as an attribute of the target object. There's also a shorthand for this method as a AsyncMachine.constructor's param.
Target object.
class Foo {
constructor() {
this.states = machine(['A', 'B', 'C'])
this.states.setTarget(this)
this.states.add('A')
}
A_state() {
console.log('State A set')
}
}
Returns the JSON structure of states along with their relations.
JSON version of the current machine. Valid input for the machine factory.
Returns a string representation of the machine (name and the list of states), optionally including the in-active states.
Returns the list of target states which are about to be active after the transition finishes.
Requires [[duringTranstion]] to be true or it'll throw.
Returns a string representation of the machine using the statesToString method.
TODO desc TODO sample TODO test
Resolves the returned promise when all of the passed states are active (at the same time). Accepts an optional abort function.
Example
const example = machine(['A', 'B', 'C'])
example.when(['A', 'B']).then( () => {
console.log('A, B')
}
example.add('A')
example.add('B') // prints 'A, B'
TODO support push cancellation
List of state names
Existing abort function (optional)
Promise resolved once all states are set simultaneously.
Resolves the returned promise when all of the passed states are NOT active (at the same time). Accepts an optional abort function.
Example
const example = machine(['A', 'B', 'C'])
example.when(['A', 'B']).then( () => {
console.log('A, B')
}
example.add('A')
example.add('B') // prints 'A, B'
TODO support push cancellation
TODO merge with when(active, inactive)
List of state names
Existing abort function (optional)
Promise resolved once all states are set simultaneously.
Exception state's properties. See Exception_state final transition handler.
Generated using TypeDoc
Base class to extend. Define states as prototype attributes or inside of the constructor. In the latter case remember to call this.registerAll() afterwards (for every sub constructor).
The Exception state is already provided.
class FooStates extends AsyncMachine { Enabled: {} Downloading: { drop: 'Downloaded' } Downloaded = { drop: 'Downloading' } constructor(target) { super(target) this.registerAll() } } class Foo { constructor() { this.states = new FooStates(this) } Downloading_state(states, url) { fetch(url, this.states.addByCallack('Downloaded')) } Downloaded_state(states, local_path) { console.log(`Downloaded ${this.url} to ${local_path}`) } }