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