Xstate: Assign actions being "raised" break SCXML-compatibility

Created on 8 Aug 2019  路  6Comments  路  Source: davidkpiano/xstate

Bug

Description:

In SCXML <assign/> has no special traits over other executable content and thus it is expected to be evaluated according to block order.

Current XState semantics (assign actions being processed before other executable content) makes it impossible to pass i.e. this test - https://gitlab.com/scion-scxml/test-framework/blob/master/test/w3c-ecma/test159.txml.scxml

bug 馃搼 SCXML

Most helpful comment

It's certainly worth exploring - even if only as a trial implementation of the new algorithm. For now, I'm focusing on going through all w3c tests and trying to enable as many as I can and also after that I might focus on other things, but this is certainly something I'd like to revisit in the future.

All 6 comments

Yeah, the processing of actions should be refactored so that instead of partitioning, they're simply processed in-order. Of course, processing of executable actions in machine.transition should be a no-op (and just pass it to state.actions).

This would be quite a breaking change as partitioning behavior is explicitly described in the docs - so fixing this would have to wait for the next major version. Nevertheless, it's worth discussing now - as it has great implications on various stuff and we should come up with an API which would allow "pure" transitions without breaking SCXML compatibility.

Executable blocks
According to SCXML each executable content is enclosed in a block and such a block is always executed during microstep. Also, an error inside a block results in that block being terminated and error.execution to be raised - but other blocks of a transition should execute as normal. This means that "block" distinction is important and we can't return back to a user flat actions.

Resolving actions
Because actions (including assigns) need to be processed in order we can't resolve actions inside Machine, but rather this should be done in Interpreter - mainly because the expressions on actions need access to updated context and such.

.transition()
At the moment it's slightly confusing as it's really more like a .macrostep() and transitions are happening also on microsteps - so the API seems to be somewhat ill-named at the moment. The intention is great behind it - it tries to lift some weight of managing internalQueue, eventless transitions etc from the shoulders of developer.

However it really doesn't seem to me possible to have both here:

  • .macrostep()-like API on the Machine
  • SCXML compatibility

It looks like we have to unwind responsibilities here a little bit and put more into Interpreter - having Machine mainly responsible for a single transition at a time.
This means that i.e. a direct Machine consumer would have to manage internalQueue, but that's not necessarily a bad thing - if Machine consumer has responsibility of executing the actions they have also have access to the internalQueue as they need to be able to put error.execution into it.

I agree, we would have to match SCXML execution behavior for V5.

For now, it might be helpful to have a new machine.microstep(state, event) method that returns a new intermediate State which represents the result of a raised event occurring on state. A flag can be set on the interpreter to use SCXML execution order (document order) rather than what is currently documented, without changing current behavior if the flag is not set (for now).

What do you think?

It's certainly worth exploring - even if only as a trial implementation of the new algorithm. For now, I'm focusing on going through all w3c tests and trying to enable as many as I can and also after that I might focus on other things, but this is certainly something I'd like to revisit in the future.

Very happy to hear this getting fixed in v5. 馃槏

Does a version already exist where assign actions are not prioritized?

I have an event that deletes an actor by executing three actions.

  1. Sending an event to the child machine.
  2. Sending the stop event.
  3. Calling assign to remove the ref from context.

Because action three is executed first, the two events are never sent because I've already lost the ref to the child by the time they are executed.

If there is no sneak peek for v5 yet, would you recommend me to execute actions 1 and 2 within the assign action (feels rather dirty but I suppose it'd work), or move the third assign action into a completely different event and instead use the send action creator to ensure assign is executed last?

Edit: The event that is sent to the child machine is used to perform some cleanup actions. See this spectrum post for extra context.

Once v5/microstep is merged in, that will be the "version" that has that applied.

For now, go ahead and execute those actions in assign().

Was this page helpful?
0 / 5 - 0 ratings

Related issues

suku-h picture suku-h  路  3Comments

doup picture doup  路  3Comments

carlbarrdahl picture carlbarrdahl  路  3Comments

hnordt picture hnordt  路  3Comments

carloslfu picture carloslfu  路  3Comments