javascript-state-machine icon indicating copy to clipboard operation
javascript-state-machine copied to clipboard

transition is invalid while previous transition is still in progress

Open tczzz opened this issue 8 years ago • 4 comments

This code is an example of what I read Asynchronous Transitions.md。 But did not work properly and what should I do?

var fsm = new StateMachine({
  init: 'menu',

    transitions: [
      { name: 'play', from: 'menu', to: 'game' },
      { name: 'quit', from: 'game', to: 'menu' }
    ],

    methods: {

      onEnterMenu: function() {
        console.log('onEnterMenu')
        return new Promise(function(resolve, reject) {
          setTimeout(function () {
            resolve()
          }, 1000)
        })
      },

      onEnterGame: function() {
        console.log('onEnterGame')
        return new Promise(function(resolve, reject) {
          setTimeout(function () {
            resolve()
          }, 1000)
        })
      },

      onLeaveMenu: function() {
        console.log('onLeaveMenu')
        return new Promise(function(resolve, reject) {
          setTimeout(function () {
            resolve()
          }, 1000)
        })
      },

      onLeaveGame: function() {
        console.log('onLeaveGame')
        return new Promise(function(resolve, reject) {
          setTimeout(function () {
            resolve()
          }, 1000)
        })
      }
    }
})

fsm.play()

tczzz avatar Aug 09 '17 03:08 tczzz

By the time you call fsm.play() the promise inside the onEnterMenu method is not yet resolved. Thus, there is already a pending transition and an exception is thrown as explained in error-handling.md

The way that worked for me, was to explicitly specify an initial state:

var fsm = new StateMachine({
  // init: 'menu',
  transitions: [
    { name: 'init', from: 'none', to: 'menu' },
    { name: 'play', from: 'menu', to: 'game' },
    { name: 'quit', from: 'game', to: 'menu' }
  ],
  ...
});

...and then explicitly initialize:

fsm.init().then(fsm.play.bind(fsm));

This way, you can make sure that the transition to state game will take place after the transition to state menu has been completed.

georapbox avatar Aug 19 '17 13:08 georapbox

@georapbox thank you for the explanation! I understand that

tczzz avatar Aug 20 '17 02:08 tczzz

Guys, this does not work for me. I'm using the StateMachine.factory, does this make a difference?

beshur avatar May 19 '18 16:05 beshur

const fsm = require('javascript-state-machine');

let fsmFactory = fsm.factory({
  init: 'parse',
  data: function(input) {
    return {
      rawInput: input,
      command: '',
      args: []
    }
  },
  transitions: [
    { name: 'doParse', from: 'none', to: 'parse' },
    { name: 'doParseSingle', from: 'parse', to: 'parseSingle' },
    { name: 'doParseMultiple', from: 'parse', to: 'parseMultiple' },
    { name: 'toEmptyInput', from: 'parse', to: 'emptyInput'}
  ],
  methods: {
    onBeforeTransition: function() {
      console.log('onBeforeTransition', arguments);
    },
    onParse: function(state) {
      let toParse = this.rawInput;
      console.log('onParse', toParse);
      if (!toParse) {
        this.toEmptyInput();
      } else {
        this.splitInput(toParse);
      }
    },
    splitInput: function(toParse) {
      let input = toParse.split(' ');
      this.command = input.shift();
      if (input.length) {
        this.args = input;
        this.doParseMultiple();
      } else {
        this.doParseSingle();
      }
    }
  }
});

let fsm1 = new fsmFactory('/start');

beshur avatar May 19 '18 16:05 beshur