Optionaloptions: { afterEmitting: string }
Test if a certain state will be entered after emitting an event. Works only after using TStatebotFsm.performTransitions.
Whether the transition is possible.
import { Statebot } from 'statebot'
let machine = Statebot('game-menus', {
chart: `
loading ->
menu ->
play |
options |
sound |
quit
// Go back to menu
play | options | sound -> menu
// Can quit from main game, too
play -> quit
`
})
machine.canTransitionTo('play')
// false
machine.enter('menu')
machine.canTransitionTo(['play', 'options'])
// true
machine.canTransitionTo('play', {
afterEmitting: 'startGame'
})
// false
Immediately emits an event, firing any listeners added using TStatebotFsm.performTransitions or TStatebotFsm.onEvent.
Optional...args: any[]
Optional arguments to pass to listeners.
Whether or not the event had listeners.
import { Statebot } from 'statebot'
let machine = Statebot('basic-form', {
chart: `
idle -> sending -> redirect
`
})
machine.performTransitions({
'idle -> sending': {
on: 'post-data',
then: (...args) => {
console.log('Event args: ', args)
// setTimeout(machine.Enter('redirect'), 5000)
}
}
})
machine.emit('post-data', 'Hello, world!')
// Event args: ["Hello, world!"]
machine.currentState()
// "sending"
Creates a function that emits the specified event.
(This is essentially a convenience wrapper around TStatebotFsm.emit.)
The desired event to TStatebotFsm.emit.
Optional...curriedArgs: any[]
Arguments that will curry into the returned emit()
function whenever it is called.
A function that emits that event.
import { Statebot } from 'statebot'
let machine = Statebot('traffic-lights', {
chart: `
go ->
prepare-to-stop ->
stop
// ...gotta keep that traffic flowing
stop ->
prepare-to-go ->
go
`,
startIn: 'stop'
})
machine.performTransitions({
'stop -> prepare-to-go': { on: 'timer' },
'prepare-to-go -> go': { on: 'timer' },
'go -> prepare-to-stop': { on: 'timer' },
'prepare-to-stop -> stop': { on: 'timer' }
})
let nextTrafficLight = machine.Emit('timer')
machine.currentState()
// "stop"
nextTrafficLight()
nextTrafficLight()
nextTrafficLight()
machine.currentState()
// "prepare-to-stop"
Immediately changes to the specified state, so long as it is accessible from the TStatebotFsm.currentState.
The desired state to switch-to.
Optional...args: any[]
Optional arguments to pass to transition callbacks.
Whether or not the state changed.
import { Statebot } from 'statebot'
let machine = Statebot('dialog', {
chart: `
idle -> showing-modal -> (saving | idle)
saving -> idle
`
})
machine.currentState()
// "idle"
machine.enter('saving')
// false
// Statebot[dialog]: Invalid transition "idle->saving", not switching
// > Previous transition: "[undefined]->idle"
// > From "idle", valid states are: ["showing-modal"]
machine.enter('showing-modal')
// true
Creates a function that changes to the specified state, so long as it is accessible from the TStatebotFsm.currentState.
(This is essentially a convenience wrapper around TStatebotFsm.enter.)
The desired state to switch-to.
Optional...curriedArgs: any[]
Arguments that will curry into the returned
enter() function whenever it is called.
A function that can change the state when called.
import { Statebot } from 'statebot'
let machine = Statebot('popup-menu', {
chart: `
idle -> menu-opened ->
(item-clicked | idle)
item-clicked -> idle
`,
startIn: 'menu-opened'
})
button.onclick = machine.Enter('item-clicked')
machine.currentState()
// "menu-opened"
button.onclick()
machine.currentState()
// "item-clicked"
Returns all states the machine has been in so far, up to a limit set
by historyLimit in TStatebotOptions.
A copy of the state-history.
import { Statebot } from 'statebot'
let machine = Statebot('downloader', {
chart: `
loading -> (failure | success)
failure -> loading
success -> done
`,
historyLimit: 4
})
machine.enter('failure')
machine.enter('loading')
machine.enter('success')
machine.enter('done')
machine.history()
// ["failure", "loading", "success", "done"]
Print information about the current machine to the console.
import { Statebot } from 'statebot'
let machine = Statebot('half-duplex', {
chart: `
idle -> sending | receiving -> done
`
})
machine.info()
// [half-duplex]: Information about this state-machine.
// [half-duplex]: Listening for the following state-changes:
// ┌---------┬-------------┬--------┐
// │ (index) │ states │ # │
// ├---------┼-------------┼--------┤
// │ 0 │ 'done' │ 'None' │
// │ 1 │ 'idle' │ 'None' │
// │ 2 │ 'receiving' │ 'None' │
// │ 3 │ 'sending' │ 'None' │
// └---------┴-------------┴--------┘
// [half-duplex] Listening for the following transitions:
// ┌---------┬-------------------┬--------┐
// │ (index) │ transitions │ # │
// ├---------┼-------------------┼--------┤
// │ 0 │ 'idle->receiving' │ 'None' │
// │ 1 │ 'idle->sending' │ 'None' │
// │ 2 │ 'receiving->done' │ 'None' │
// │ 3 │ 'sending->done' │ 'None' │
// └---------┴-------------------┴--------┘
// [half-duplex]: Listening for the following events:
// (No information)
Get information about the current machine.
Same details as TStatebotFsm.info in object-form.
An object containing information about the current machine.
import { Statebot } from 'statebot'
let machine = Statebot('half-duplex', {
chart: `
idle -> sending | receiving -> done
`
})
machine.inspect()
// Will return an object with the following signature:
// { states, transitions, events }
// These will each have key-values, the key being the name
// and the value being the number of listeners attached.
Checks if the TStatebotFsm.currentState
matches the specified state, immediately returning either
true or false.
An object can be used instead of a string, with the keys
being the states, and the values corresponding to their
outputWhenTrue value. See the example.
If outputWhenTrue is specified, then it will be returned
instead of true, and null will be returned instead of
false. If a function is specified, then its return-value
will be used as the true-value.
The state to test against. This can be a string if you have a single condition, or an object for multiple. (See example.)
import { Statebot } from 'statebot'
let machine = Statebot('little-revver', {
chart: `
idle ->
(gear-1 | gear-2 | reverse) ->
idle
`
})
machine.inState('idle')
// true
machine.inState('idle', 'Purrrr...')
// "Purrrr..."
machine.enter('gear-1')
machine.inState({
'idle': 'Purrrr...',
'gear-1': () => 'Chugga-chugga-chugga...',
'gear-2': () => 'Brumma-brumma-brum-brum...',
'reverse': false,
})
// "Chugga-chugga-chugga..."
machine.inState('idle', () => {
console.log('Idling!')
return 'Purrrr...'
})
// null
// ^ the function is not called at all in the `false` case,
// so no console.log either.
OptionaloutputWhenTrue: anyReturns a function which, when run, tests that
TStatebotFsm.currentState matches the
specified state, returning either true or false.
If outputWhenTrue is specified, then it will be returned
instead of true, and null will be returned instead of
false.
(This is essentially a convenience wrapper around TStatebotFsm.inState.)
The state to test against.
A function that calls TStatebotFsm.inState.
import { Statebot } from 'statebot'
let machine = Statebot('little-revver', {
chart: `
idle ->
(gear-1 | gear-2 | reverse) ->
idle
`
})
let idling = machine.InState('idle')
let purring = machine.InState('idle', () => {
console.log('Idling!')
return 'Purrrr...'
})
idling()
// true
purring()
// Idling!
// "Purrrr..."
machine.enter('gear-1')
purring()
// null
// ^ the function is not called at all in the `false` case,
// so no console.log either.
OptionaloutputWhenTrue: anyReturns the name of the state-machine.
Used for logging and also by assert.assertRoute for the same.
The name of the state-machine.
Adds a listener that runs a callback immediately AFTER the specified-state becomes the current one.
A function is returned that will remove the listener.
The state.
A callback function with the signature:
(fromState, ...args?)
A function that removes the listener.
Function
Adds a listener that runs a callback immediately BEFORE the specified-state becomes the current one.
A function is returned that will remove the listener.
The state.
A callback function with the signature:
(fromState, ...args?)
A function that removes the listener.
import { Statebot } from 'statebot'
let machine = Statebot('half-duplex', {
chart: `
idle -> sending | receiving -> done
`
})
machine.onEntered('done', () => {
console.log('We made it!')
})
machine.onEntering('done', fromState => {
console.log('Entering from:', fromState)
})
machine.enter('sending')
machine.enter('done')
// Entering from: sending
// We made it!
Adds a listener that runs a callback immediately after the specified event is called.
A function is returned that will remove the listener.
The callback.
A function that removes the listener.
import { Statebot } from 'statebot'
let machine = Statebot('traffic-lights', {
chart: `
go ->
prepare-to-stop ->
stop
// ...gotta keep that traffic flowing
stop ->
prepare-to-go ->
go
`
})
machine.performTransitions({
'stop -> prepare-to-go -> go': { on: 'timer' },
'go -> prepare-to-stop -> stop': { on: 'timer' },
})
machine.onEvent('timer', () => {
redrawTrafficLights()
})
setInterval(machine.Emit('timer'), 2000)
Adds a listener that runs a callback immediately AFTER the specified-state is no longer the current one.
A function is returned that will remove the listener.
The state.
A callback function with the signature:
(toState, ...args?)
A function that removes the listener.
Adds a listener that runs a callback immediately BEFORE the specified-state is no longer the current one.
A function is returned that will remove the listener.
The state.
A callback function with the signature:
(toState, ...args?)
A function that removes the listener.
import { Statebot } from 'statebot'
let machine = Statebot('half-duplex', {
chart: `
idle -> sending | receiving -> done
`
})
machine.onExited('idle', () => {
console.log('Peace out!')
})
machine.onExiting('idle', toState => {
console.log('Heading to:', toState)
})
machine.enter('receiving')
machine.enter('done')
// Heading to: receiving
// Peace out!
Adds a listener that runs a callback immediately after ANY state-change.
A function is returned that will remove the listener.
A callback function with the signature:
(toState, fromState, ...args?)
A function that removes the listener.
Adds a listener that runs a callback immediately before ANY state-change.
A function is returned that will remove the listener.
A callback function with the signature:
(toState, fromState, ...args?)
A function that removes the listener.
Run callbacks when transitions happen.
If a callback returns a function, it will be invoked when the state is exited in the same manner as if an TStatebotFsm.onExiting handler was created using it.
Configuration in the form of an object, or a function that returns an object. If a function is used, there will be a single argument passed-in: an object with the following methods attached as a convenience:
A function that removes all listeners added by this method.
import { Statebot } from 'statebot'
let machine = Statebot('half-duplex', {
chart: `
idle -> sending | receiving -> done
`
})
machine.onTransitions({
'idle -> sending': () => {
sendData()
.then(machine.Enter('done', 'sent'))
.catch(machine.Enter('done', 'failed'))
},
'idle -> receiving': () => {
receiveData()
.then(machine.Enter('done', 'received'))
.catch(machine.Enter('done', 'failed'))
},
'sending | receiving -> done': whatHappened => {
console.log('All finished: ', whatHappened)
}
})
machine.enter('sending')
function sendData() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000)
setTimeout(reject, 750 + Math.round(Math.random() * 750))
})
}
function receiveData() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000)
setTimeout(reject, 750 + Math.round(Math.random() * 750))
})
}
// The above example using a function for config
machine.onTransitions(({ Enter }) => ({
'idle -> sending': () => {
sendData()
.then(Enter('done', 'sent'))
.catch(Enter('done', 'failed'))
},
'idle -> receiving': () => {
receiveData()
.then(Enter('done', 'received'))
.catch(Enter('done', 'failed'))
},
'sending | receiving -> done': whatHappened => {
console.log('All finished: ', whatHappened)
}
}))
// etc...
Pause the machine. TStatebotFsm.emit and TStatebotFsm.enter will be no-ops until the machine is TStatebotFsm.resume'd.
Returns true if the machine is TStatebotFsm.pause'd
Return the state the machine will be in after TStatebotFsm.emit'ing the specified event.
Works only after using TStatebotFsm.performTransitions.
See also: TStatebotFsm.canTransitionTo.
OptionalstateObject: any
If stateObject is undefined, .peek() defaults to returning
TStatebotFsm.currentState
if the event will NOT trigger a transition. Otherwise,
stateObject will be used as a key/value lookup, with key
being the predicted state, and value being the corresponding
literal or function to be run and its value returned.
import { Statebot } from 'statebot'
let machine = Statebot('peek-a-boo', {
chart: `
idle -> running
`
})
machine.performTransitions({
'idle -> running': {
on: 'start'
}
})
machine.peek('start')
// "running"
machine.peek('start', {
'running': () => 'will be in the running state'
})
// "will be in the running state"
machine.peek('unknown')
// "idle"
// Logs: Statebot[peek-a-boo]: Event not handled: "unknown"
machine.peek('unknown', {
'running': () => 'will be in the running state'
})
// null
// Logs: Statebot[peek-a-boo]: Event not handled: "unknown"
machine.emit('start')
machine.peek('start')
// "running"
// Logs: Statebot[peek-a-boo]: Will not transition after emitting: "start"
Perform transitions when events happen.
Use then to optionally add callbacks to those transitions.
If a then method returns a function, it will be invoked when
the state is exited in the same manner as if an TStatebotFsm.onExiting
handler was created using it.
Configuration in the form of an object, or a function that returns an object. If a function is used, there will be a single argument passed-in: an object with the following methods attached as a convenience:
A function that removes all listeners added by this method.
import { Statebot } from 'statebot'
let machine = Statebot('complex-form', {
chart: `
idle ->
update
// Maybe things take a long time...
update ->
waiting -> waiting-a-while
// Which path will we take?
waiting | waiting-a-while ->
success | failed | timeout
// All done!
success | failed | timeout ->
done
`
})
machine.performTransitions(({ Enter, emit }) => ({
'idle -> update': {
on: 'user-saved',
then: (data) => {
console.log('Sending data: ', data)
sendData(data)
.then(Enter('success'))
.catch(Enter('failed'))
emit('data-sent')
}
},
'update -> waiting': {
on: 'data-sent',
then: () => {
setTimeout(Enter('waiting-a-while'), 750)
setTimeout(Enter('timeout'), 5000)
}
}
}))
// Just to illustrate that you can mix n' match with onTransitions:
machine.onTransitions({
'waiting | waiting-a-while -> success': () => {
console.log('Lovely!')
},
'waiting | waiting-a-while -> timeout': () => {
console.log('Well, at least you have your shoes')
}
})
machine.emit('user-saved', ['some', 'data'])
// Sending data: ["some", "data"]
function sendData() {
return new Promise((resolve, reject) => {
setTimeout(resolve, 1000)
setTimeout(reject, 750 + Math.round(Math.random() * 750))
})
}
Returns the previous state.
The previous state, or undefined if there isn't one (ie; you
have just called TStatebotFsm.reset, or the
machine has just started.)
Returns the state-machine to its starting-state and clears the state-history.
All listeners will still be attached, but no events or transitions will be fired. The pause-state will be maintained.
Resume a TStatebotFsm.pause'd machine.
Return an array of states accessible from the state specified.
If no state is passed-in, the TStatebotFsm.currentState is used.
Optionalstate: string
The state to check. TStatebotFsm.currentState if unspecified.
Tests to see if we can transition to the specified state from the TStatebotFsm.currentState.
If more than one state is specified,
trueis returned only if ALL states are available.See also: TStatebotFsm.peek.
Can test if a certain state will be entered after emitting an event. Use
{ afterEmitting: 'eventName' }as the second argument. Works only after using TStatebotFsm.performTransitions.