I am trying to make a state invoke an asynchronous function.
I would like to trigger an event change and move to the next line after the event is resolved.
There is a snippet bellow. Consider for an example scenario a express route that would receive some payload, do some events on the state machine and after all done return the result.
How would I go about waiting an specific sent event to be resolved? Is there a straightforward way?
import { assign, createActor, fromPromise, setup } from "xstate";
const insertOnDb = async (email: string) => {
return await new Promise<{ success: boolean; data: { id: number } }>(
(resolve, reject) => {
setTimeout(() => resolve({ success: true, data: { id: 13 } }), 1000);
}
);
};
const exampleMachine = setup({
types: {
context: {} as {
email: string;
dbId?: number;
},
},
actors: {
insertOnDb: fromPromise<any, { email: string }>(async ({ input }) => {
const result = await insertOnDb(input.email);
return result.data.id;
}),
},
actions: {
setFailed: assign({ failed: true }),
setInserted: assign({ insertedOnDb: true }),
},
}).createMachine({
context: { email: "emailœ@gmail.com" },
initial: "Considered",
strict: true,
on: {
"*": {
// unknown event
actions: ({ event }) => {
throw new Error(`Unknown or guarded event: ${event.type}`);
},
},
},
states: {
Considered: {
on: {
REJECT: {
target: "Rejected",
},
APPROVE: {
target: "InsertingOndb"
},
},
},
InsertingOndb: {
invoke: {
id: "insertOnDb",
src: "insertOnDb",
input: ({ context: { email } }) => ({ email }),
onDone: {
target: "Inserted",
actions: [
assign({
dbId: ({ event }) => {
return event.output;
},
}),
"setInserted",
],
},
onError: {
target: "Failure",
actions: "setFailed",
},
},
},
Inserted: {},
},
});
const exampleActor = createActor(exampleMachine).start();
exampleActor.send({ type: "APPROVE" });
// Would like here to only go to a next line after APPROVE event is fully resolved.
// something like await exampleActor.send({ type: 'APPROVE' }); would have been great
Thanks in advance