-
Is there a way to wait for an event that will trigger an asynchronous actor to be completed? I have a snippet to try to exemplify what I need 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 resolve and the machine went to either Inserted or Failure state
// something like await exampleActor.send({ type: 'APPROVE' }); would have been great Background: I want to use a state machine within a series of lambdas and would like to be able to do it with async await, or figure out the best way to do it. Thanks in advance |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
@drFabio you might find the await waitFor(exampleActor, (state) => state.matches('Inserted')) Timeout is configurable (default 10s). In my experience, this means that a failed actor won't immediately result in a rejected await waitFor(exampleActor, (state) => {
if (state.matches('Failure')) throw new Error('Insert failed')
state.matches('Inserted')
}) |
Beta Was this translation helpful? Give feedback.
-
Dear @crishoj this worked perfectly except for waiting for a final state, on this scenario it throws
Should I use something else for final states? how could I wait for a machine to reach some final state? |
Beta Was this translation helpful? Give feedback.
@drFabio you might find the
waitFor
helper useful:Timeout is configurable (default 10s).
In my experience, this means that a failed actor won't immediately result in a rejected
waitFor
promise. If you wantwaitFor
to abort as soon as the actor reaches theFailure
state, you can expand the awaited predicate with athrow
: