Invoke a service from a "serverless" fsm (Xstate)

559 views Asked by At

I'm using xsate on a Node.JS backend. Here is the current flow :

  • State is rehydrated (Initialized or fetched from DB)
  • Event is sent to the FSM
  • State is serialized to DB

Here is some pseudo code

const state  = db.fetch(flowId) ?? machine.initialState;
 // Use State.create() to restore state from a plain object
const previousState = State.create<DefaultContext, MyEvent>(stateDefinition);
// Use machine.resolveState() to resolve the state definition to a new State instance relative to the machine
const resolvedState = machine.resolveState(previousState);
const interpreter = interpret(machine).start(resolvedState);
onst newState: State<DefaultContext, MyEvent, any, Typestate<DefaultContext>> = interpreter.send(event);
db.saveState(flowId, newState);

My question is : Is it possible to Invoke a Promise ?

I would like to keep my FSM "alive" if I have pending promises. The goal is to modify the context based on the promise result. Is there some hook I could use ?

Thanks for any advise.

2

There are 2 answers

0
badboy99 On BEST ANSWER

I have the same use case as you. So what I do is add an extra property to context, say waitingAsync. Set it to true on the state entry, and set it to false on done.

Follow the Invoking Promises example with some modifications:

// ...
loading: {
  // set waitingAsync to true
  entry: assign((context) => ({...context, waitingAsync: true})),
  invoke: {
    id: 'getUser',
    src: (context, event) => fetchUser(context.userId),
    onDone: {
      target: 'success',
      actions: [
        assign({ user: (context, event) => event.data })
        // set waitingAsync to false
        assign((context) => ({...context, waitingAsync: false}))
      ]
    },
  }
},
// ...

Then you can use the waitFor helper to wait until the promise is done.

const interpreter = interpret(machine).start();
interpreter.send(event);
await waitFor(interpreter, (state) => !state.context.waitingAsync);
1
David Khourshid On

You can now use the new waitFor(...) helper in the latest version of XState to asyncronously wait for a state machine to reach some condition, like a specific state.

In your situation, the predicate would be something like state.matches('yourSuccessState').