Closed RainerAtSpirit closed 5 years ago
Copying the reply here:
I haven't seen anything in SCXML that would notify "when all invoked services are done" although you can accomplish this with "when all parallel states are done" - xstate.js.org/docs/guides/final.html#parallel-states
That might be a bit verbose, but it's for good reason - parallel states can have invoked services on each of them, but they can also be normal states without invoked services.
You can create a helper function that cuts down on the extra config, something like:
states: {
pending: {
...parallelServices(svc1, svc2, svc3),
onDone: { /* ... */ }
}
}
I was able to implement that feature in userland by adding an invokeCount
and a transient
transition.
Would it be possible that xstate support this scenario OOTB?
export const serviceMachine = Machine<
IServiceMachineContext,
IServiceMachineSchema,
EventObject
>({
id: "serviceMachine",
initial: "pending",
context: {
invokeCount: 0,
topTenProjects: undefined,
fundingTypes: undefined
},
states: {
pending: {
on: {
"": [
{
target: "final",
cond: (ctx, event) => ctx.invokeCount === 2
}
]
},
invoke: [
{
id: "topTenProjects",
src: topTenProjects,
onDone: {
actions: assign((ctx, event) =>
produce(ctx, draft => {
draft.topTenProjects = event.data
draft.invokeCount = ctx.invokeCount + 1
})
)
}
},
{
id: "fundingTypes",
src: fundingTypes,
onDone: {
actions: assign((ctx, event) =>
produce(ctx, draft => {
draft.fundingTypes = event.data
draft.invokeCount = ctx.invokeCount + 1
})
)
}
}
]
},
final: {}
}
})
Would it be possible that xstate support this scenario OOTB?
As much as I'd love to, every single API design decision must be fully 1-1 compatible with SCXML: https://www.w3.org/TR/scxml/ . Remember: XState is nothing new invented.
I would tackle this a different way:
pending: {
invoke: {
src: (ctx, event) => Promise.all([
/* all services */
]),
onDone: 'final'
}
}
Sounds fair. Thanks for sharing a different approach.
After reading up a bit on invoke your suggestion of using parallel machines with a single invoke per state is favorable to any "workarounds" to make invoke arrays work the way I'd expect them to work.
@davidkpiano Would it make sense to add that limitation the invoke
array documentation?
For sake of completeness here's the above refactored example.
export const serviceMachine = Machine<
IServiceMachineContext,
IServiceMachineSchema,
EventObject
>(
{
id: "serviceMachine",
initial: "pending",
context: {
topTenProjects: undefined,
fundingTypes: undefined
},
states: {
pending: {
type: "parallel",
states: {
topTenProjects: {
initial: "loading",
states: {
loading: {
invoke: {
src: "topTenProjects",
onDone: {
target: "done",
actions: assign((ctx: IServiceMachineContext, event) =>
produce(ctx, draft => {
draft.topTenProjects = event.data
})
)
}
}
},
done: {
type: "final"
}
}
},
fundingTypes: {
initial: "loading",
states: {
loading: {
invoke: {
src: "fundingTypes",
onDone: {
target: "done",
actions: assign((ctx: IServiceMachineContext, event) =>
produce(ctx, draft => {
draft.fundingTypes = event.data
})
)
}
}
},
done: {
type: "final"
}
}
}
},
onDone: {
target: "final"
}
},
final: {}
}
},
{
services: {
topTenProjects,
fundingTypes
}
}
)
Would it be possible that xstate support this scenario OOTB?
As much as I'd love to, every single API design decision must be fully 1-1 compatible with SCXML: https://www.w3.org/TR/scxml/ . Remember: XState is nothing new invented.
I would tackle this a different way:
pending: { invoke: { src: (ctx, event) => Promise.all([ /* all services */ ]), onDone: 'final' } }
How would you parameterise those promises? Imagine I have parameterised fetchUser
and fetchData
e.g.. Now I want to use those in a Promise.all
. Is that possible?
Combinators (like Promise.all
) are not supported out of the box - you'd need to handle that yourself, for example by just externalizing your promises and calling to them directly.
Is there a way to wait till all invoked services have reached their final state before moving on to the next state? In the example below I'd like all childMachines be in their final state before moving on to the
success
state. Putting target: "success" into the onDone configuration doesn't do the job as the first response will move tosuccess
ignoring all other responses that are still in flight.Originally posted by @RainerAtSpirit in https://github.com/davidkpiano/xstate/issues/321#issuecomment-461802330