javascript-state-machine
javascript-state-machine copied to clipboard
Throwing on async transition doesn't cancel transition
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 https://github.com/jakesgordon/javascript-state-machine/blob/0d603577423244228cebcd62e60dbbfff27c6ea3/src/jsm.js#L157 to:
.catch((err) => {
console.log('called catch')
this.failTransit(err);
})
And adding a console.log(event)
right before https://github.com/jakesgordon/javascript-state-machine/blob/0d603577423244228cebcd62e60dbbfff27c6ea3/src/jsm.js#L148 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.
Yes. I confirm this situation. Which is a shame. The only solution would be to return false in on of the above mentioned callbacks.
Will this be fixed ? Is this library dead ?