Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throwing on async transition doesn't cancel transition #171

Open
negebauer opened this issue Nov 4, 2018 · 2 comments
Open

Throwing on async transition doesn't cancel transition #171

negebauer opened this issue Nov 4, 2018 · 2 comments

Comments

@negebauer
Copy link

According to the Asynchronous Transitions doc

Returning a Promise from a lifecycle event will cause the lifecycle for that transition to pause. It can be continued by resolving the promise, or cancelled by rejecting the promise.

Rejecting a promise returned in a lifecycle event should cancel the transition, as it happens when returning false on some lifecycle events like onTransition and onBeforeTransition.

But rejecting the returned promise doesn't have this effect, the transition completes as if the promise was resolved correctly, which can be demonstrated with this code

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

const stateMachine = new StateMachine({
  init: 'open',
  transitions: [
    { name: 'close', from: 'open', to: 'closed' },
    { name: 'requireInfo', from: 'open', to: 'requiresInfo' },
  ],
  methods: {
    async onClose() {
      throw new Error('failed close')
    },
    async onRequireInfo() {
      return 'transitioned to require info'
    }
  }
})

async function test() {
  console.log('stateMachine.state:', stateMachine.state)
  try {
    const result = await stateMachine.close()
    console.log('stateMachine.state:', stateMachine.state)
  } catch (error) {
    console.log('error:', error.message)
    console.log('stateMachine.state:', stateMachine.state)
  }
  try {
    const result = await stateMachine.requireInfo()
    console.log('stateMachine.state:', stateMachine.state)
  } catch (error) {

    console.log('error:', error.message)
    console.log('stateMachine.state:', stateMachine.state)
  }
}

test()

Output:

stateMachine.state: open
error: failed close
stateMachine.state: closed
error: transition is invalid in current state
stateMachine.state: closed

The code above should end up transitioning the state from open to requiresInfo, but instead it perform the close transition. There's no actual waiting on the promise, as the catch is only called after all lifecycle events trigger. Changing

.catch(this.failTransit.bind(this))
to:

                  .catch((err) => {
                    console.log('called catch')
                    this.failTransit(err);
                  })

And adding a console.log(event) right before

if (observers.length === 0) {
shows the following output

stateMachine.state: open   # right before calling stateMachine.close()
onBeforeTransition
onBeforeClose
onLeaveState
onLeaveOpen
onTransition
doTransit
doTransit
onEnterState
onEnterClosed
onClosed
onAfterTransition
onAfterClose
onClose
called catch # here the catch is handled, no other lifecycle event should trigger while the promise is still pending
error: failed close
stateMachine.state: closed

When writing this issue I actually noticed that the problem is that there's no waiting on the resolve or reject of a promise, the other lifecycleEvents are triggered no matter what happens with the promise.

@NelsonFrancisco
Copy link

Yes.
I confirm this situation. Which is a shame.
The only solution would be to return false in on of the above mentioned callbacks.

@LeoMartinDev
Copy link

Will this be fixed ? Is this library dead ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants