import Promise from 'bluebird';
export interface Transition {
name: string,
from: string,
to: string
}
export class StateMachine {
private previousState : string;
private currentState : string;
private transitions : Transition[];
private transitionCallbacks : any = {};
private enterCallbacks : any = {};
private leaveCallbacks : any = {};
constructor(intitialState : string, transitions: Transition[])
{
this.previousState = intitialState;
this.currentState = intitialState;
this.transitions = transitions;
}
private changeState(transitionName : string, newState : string) {
console.log(`${transitionName}: ${this.currentState} -> ${newState}`);
this.previousState = this.currentState;
this.currentState = newState;
let cb;
cb = this.leaveCallbacks[this.previousState];
if (cb) {
setTimeout(cb, 0);
}
cb = this.enterCallbacks[this.currentState];
if (cb) {
setTimeout(cb, 0);
}
}
findTransition(transitionName: string, doThrow : boolean = false) : Transition | null {
let transitions = this.transitions.filter(t => t.name == transitionName);
if (transitions.length < 1) {
if (doThrow) {
throw new Error(`transition '${transitionName}' does not exist`);
}
return null;
}
let transition = transitions.filter(t => t.from == '*' || t.from == this.currentState).pop();
if (!transition) {
if (doThrow) {
throw new Error(`transition '${transitionName}' not possible from state '${this.currentState}'`);
}
return null;
}
return transition;
}
trigger(transitionName: string) : void {
let transition = this.findTransition(transitionName, true);
console.log(`triggered: ${transitionName}`);
let cb = this.transitionCallbacks[transitionName];
if (cb) {
Promise.method(cb)({})
.then(() => this.changeState(transitionName, transition!.to))
.catch(() => Promise.resolve());
} else {
this.changeState(transitionName, transition!.to);
}
}
canTrigger(transitionName: string) : boolean {
let transition = this.findTransition(transitionName, false);
if (!transition) {
return false;
}
return true;
}
getState() : string {
return this.currentState;
}
public on(transitionName : string, callback: Function) : void {
this.transitionCallbacks[transitionName] = callback;
}
public onEnter(stateName : string, callback: Function) : void {
this.enterCallbacks[stateName] = callback;
}
public onLeave(stateName : string, callback: Function) : void {
this.leaveCallbacks[stateName] = callback;
}
}
import Promise from 'bluebird';
import {StateMachine} from './StateMachine'
describe('StateMachine', () => {
it('should change state', (done) => {
let stateMachine = new StateMachine('created', [
{ name: 'load', from: 'created', to: 'loading'},
{ name: 'setReady', from: 'loading', to: 'ready'},
{ name: 'play', from: 'ready', to: 'playing'},
{ name: 'stop', from: 'playing', to: 'ended'},
{ name: 'cancel', from: '*', to: 'ended'}
]);
stateMachine.trigger('load');
stateMachine.on('setReady', () => Promise.delay(1000));
stateMachine.onLeave('loading', () => {
console.log('I am loaded');
});
stateMachine.onEnter('ended', () => {
console.log('I am ended');
});
stateMachine.onEnter('ready', () => {
console.log('I am ready');
stateMachine.trigger('cancel');
stateMachine.trigger('play');
stateMachine.trigger('stop');
});
stateMachine.trigger('setReady');
setTimeout(done, 3000);
})
});
const Promise = require('bluebird');
// Transition { name, from, to }
// OnStateChanged = (transition, currentState, newState) {};
class StateMachine {
constructor(intitialState, transitions, onStateChanged)
{
this.previousState = intitialState;
this.currentState = intitialState;
this.transitions = transitions;
this.onStateChanged = onStateChanged;
this.transitionCallbacks = {};
this.enterCallbacks = {};
this.leaveCallbacks = {};
}
changeState(transitionName, newState) {
this.previousState = this.currentState;
this.currentState = newState;
this.onStateChanged(transitionName, this.previousState, this.currentState);
let cb;
cb = this.leaveCallbacks[this.previousState];
if (cb) {
setTimeout(cb, 0);
}
cb = this.enterCallbacks[this.currentState];
if (cb) {
setTimeout(cb, 0);
}
}
findTransition(transitionName, doThrow) {
let transitions = this.transitions.filter(t => t.name == transitionName);
if (transitions.length < 1) {
if (doThrow) {
throw new Error(`transition '${transitionName}' does not exist`);
}
return null;
}
let transition = transitions.filter(t => t.from == '*' || t.from == this.currentState).pop();
if (!transition) {
if (doThrow) {
throw new Error(`transition '${transitionName}' not possible from state '${this.currentState}'`);
}
return null;
}
return transition;
}
trigger(transitionName) {
let self = this;
let transition = this.findTransition(transitionName, true);
let cb = this.transitionCallbacks[transitionName];
setTimeout(() => {
if (cb) {
Promise.method(cb)({})
.then(() => self.changeState(transitionName, transition.to))
.catch(() => Promise.resolve());
} else {
self.changeState(transitionName, transition.to);
}
}, 0);
}
canTrigger(transitionName) {
let transition = this.findTransition(transitionName, false);
if (!transition) {
return false;
}
return true;
}
getState() {
return this.currentState;
}
on(transitionName, callback) {
this.transitionCallbacks[transitionName] = callback;
}
onEnter(stateName, callback) {
this.enterCallbacks[stateName] = callback;
}
onLeave(stateName, callback) {
this.leaveCallbacks[stateName] = callback;
}
}
module.exports = StateMachine;
const Promise = require('bluebird');
// Transition { name, from, to }
// OnStateChanged = (transition, currentState, newState) {};
class StateMachine {
constructor(intitialState, transitions, onStateChanged) {
this.previousState = intitialState;
this.currentState = intitialState;
this.transitions = transitions;
this.onStateChanged = onStateChanged;
this.transitionCallbacks = {};
this.enterCallbacks = {};
this.leaveCallbacks = {};
}
changeState(transitionName, newState) {
this.previousState = this.currentState;
this.currentState = newState;
this.onStateChanged(transitionName, this.previousState, this.currentState);
let cb;
cb = this.leaveCallbacks[this.previousState];
if (cb) {
setTimeout(cb, 0);
}
cb = this.enterCallbacks[this.currentState];
if (cb) {
setTimeout(cb, 0);
}
}
findTransition(transitionName, doThrow) {
let transitions = this.transitions.filter(t => t.name == transitionName);
if (transitions.length < 1) {
if (doThrow) {
throw new Error(`transition '${transitionName}' does not exist`);
} else {
console.log(`transition '${transitionName}' does not exist`);
}
return null;
}
let transition = transitions.filter(t => t.from == '*' || t.from == this.currentState).pop();
if (!transition) {
if(transitions[0] && transitions[0].to == this.currentState) {
console.log(`already in state '${this.currentState}', skipping transition: '${transitionName}'`);
} else if (doThrow) {
throw new Error(`transition '${transitionName}' not possible from state '${this.currentState}'`);
} else {
console.log(`transition '${transitionName}' not possible from state '${this.currentState}'`);
}
return null;
}
return transition;
}
trigger(transitionName) {
let self = this;
let transition = this.findTransition(transitionName, false);
let cb = this.transitionCallbacks[transitionName];
if (transition) {
setTimeout(() => {
if (cb) {
Promise.method(cb)({})
.then(() => self.changeState(transitionName, transition.to))
.catch(() => Promise.resolve());
} else {
self.changeState(transitionName, transition.to);
}
}, 0);
}
}
canTrigger(transitionName) {
let transition = this.findTransition(transitionName, false);
if (!transition) {
return false;
}
return true;
}
getState() {
return this.currentState;
}
on(transitionName, callback) {
this.transitionCallbacks[transitionName] = callback;
}
onEnter(stateName, callback) {
this.enterCallbacks[stateName] = callback;
}
onLeave(stateName, callback) {
this.leaveCallbacks[stateName] = callback;
}
}
module.exports = StateMachine;
105200cookie-checkTypescript statemachine