Abort & Interruption
AFSM supports aborting an in-flight MiddleState.
abortAction
@ChangeState’s opt.abortAction aborts a MiddleState matching a specific action:
class Player extends FSM { @ChangeState(FSM.INIT, 'started', { abortAction: undefined }) async start() { await longRunning() // during this, state is 'starting' (MiddleState) }
@ChangeState('started', 'stopped', { abortAction: 'start' }) async stop() { // if currently in the 'starting' MiddleState, middle.abort(fsm) is called first }}When stop() is called while in a MiddleState with action='start':
middle.abort(fsm)is calledmiddle.aborted = truesetState(oldState, new Error("action 'start' aborted"))— state rolls back to the pre-startstatestop()’s ownfromvalidation then fails (since state went back to pre-start, which likely isn’t'started')
from: [] auto-aborts
from: [] (any state) auto-aborts an in-flight MiddleState:
@ChangeState([], 'reset')async reset() {}Regardless of the current intermediate state, it’s aborted first (middle.abort(fsm)), then forced into reset.
MiddleState.abort
class MiddleState { aborted: boolean = false abort(fsm: IFSM): void}abort(fsm):
this.aborted = truesetState.call(fsm, this.oldState, new Error("action '{action}' aborted"))- State rolls back to
oldState;stateChangedfires (with error)
After abort, if the original method is still running, its success callback checks middle.aborted:
const success = (result: any) => { fsm[cacheResult] = result if (!middle.aborted) { // if aborted, don't switch to `to` setState.call(fsm, to) opt.success?.call(this, fsm[cacheResult]) } return result}Note: abort() does not cancel the original method’s execution (it can’t cancel a Promise). It only prevents the state from switching to to and rolls back to oldState. If the method holds resources, you must clean them up yourself.
Listening to aborts
Aborts trigger stateChanged with err carrying Error("action '...' aborted"):
obj.on(FSM.STATECHANGED, (newState, oldState, err) => { if (err && err.message.includes('aborted')) { console.log('aborted:', err.message) }})Example scenarios
- User clicks “stop” to abort an in-flight “start”
- Network drops → force reset (
from: []) - Switching pages → cancel incomplete requests