xstate
xstate copied to clipboard
Bug: Actors is called before eventless actions if invoking an actor after eventless (always) transitions
XState version
XState version 5
Description
Let say i have a machine like this:
async function invokeMe() {
console.log("'next' state: should be second");
}
const myMachine = setup({
actions: {
starting: () => {
console.log("'start' state: should be first");
},
},
actors: {
invoker: fromPromise(() => invokeMe()),
},
}).createMachine({
id: "myMachine",
initial: "start",
states: {
start: {
entry: "starting",
always: "next",
},
next: {
invoke: {
id: "next",
src: "invoker",
onDone: "complete",
},
},
complete: {
type: "final",
},
},
});
Eventhough the start state is before the next state, when we run the machine, the order of the console log output was:
'next' state: should be second'start' state: should be first
Expected result
Whatever in the next state would be executed after the start state finished with its actions. So the output should be:
'start' state: should be first'next' state: should be second
Actual result
The invoked actor in the next state was executed before the start state actions finished.
Reproduction
https://stackblitz.com/edit/github-pusrgdcr
Additional context
In the codesandbox reproduction, the problem doesn't happen if the machine is waiting for the event (i.e: it waits for the user to click the button).
But if the initial prop of the machine is changed to start (i.e: it will be transitioned without waiting for any event), then the problem would occur.
p/s: I also created the reproduction in a codesandbox: https://codesandbox.io/p/sandbox/invoke-after-entry-lwqlw6
BTW, i was migrating to XState v5. In XState v4, this is working well (i.e: the actor (or formerly known as a service) will be called after the action in the eventless transition is finished).
Thanks :)
Seems like it's working as expected in XState 5.19, according to the StackBlitz link:
I'll reopen this if I'm missing anything. Thanks for the report!
Thanks @davidkpiano for the feedback :)
I think maybe I shouldn't make that kind of example in the StackBlitz :D
In the example, I just want to demonstrate that if the machine is waiting for an event, it's working. But if the machine does not have an event, then it'll have the problem.
In the feedbackMachine.ts, just change the initial to start (skipping the waiting of the event), then the problem will occur.
https://github.com/user-attachments/assets/726471ab-ba4f-4da1-961f-6f7fdb770810
I've updated the example in StackBlitz again so that it'll transition without waiting for an event and the output will be: