Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v5] Remove batching #2869

Merged
merged 19 commits into from
Jan 10, 2022
Merged

[v5] Remove batching #2869

merged 19 commits into from
Jan 10, 2022

Conversation

davidkpiano
Copy link
Member

This PR removes batching and (somewhat related) the scheduler - refactored to be a simple mailbox.

@changeset-bot
Copy link

changeset-bot bot commented Dec 9, 2021

🦋 Changeset detected

Latest commit: 0af90a3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
xstate Major
@xstate/graph Major
@xstate/immer Major
@xstate/inspect Major
@xstate/react Major
@xstate/svelte Major
@xstate/test Major
@xstate/vue Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@davidkpiano davidkpiano changed the base branch from main to next December 9, 2021 18:23
@ghost
Copy link

ghost commented Dec 9, 2021

CodeSee Review Map:

Review these changes using an interactive CodeSee Map

Review in an interactive map

View more CodeSee Maps

Legend

CodeSee Map Legend

const service = interpret(machine).start();

expect(service.state.context.childOrigin).toBe('callback_child');
interpret(machine)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: did anything change here that this test has to be asynchronous right now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, just a refactoring to match how other services are "idiomatically" tested.

Although, we should discourage reading service.state directly, and maybe disallow it in favor of service.getSnapshot()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, just a refactoring to match how other services are "idiomatically" tested.

I agree that this might be "idiomatically" correct, however, I've found out that writing "non-idiomatic" tests (aka tests executed synchronously) can reveal more edge cases while writing those tests. It's because some problems only manifest themselves when we deal with synchronous execution, ping-ponging between different actors - this is true for XState, Redux-Saga, and even RxJS because those problems can be classified as scheduling problems and all of those libs deal with this and try to manage the complexity involved with communicating different "processes" etc

davidkpiano and others added 3 commits December 11, 2021 07:58
Co-authored-by: Pat Cavit <github@patcavit.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
@@ -240,32 +240,42 @@ export class StateMachine<
return transitionNode(this.root, state.value, state, _event) || [];
}

public get first(): State<TContext, TEvent> {
const pseudoinitial = this.resolveState(
private get preInitialState(): State<TContext, TEvent> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: how do we define a "pre initial state"? could you add a comment above this method that would explain this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment. This is the initial state before evaluating any microsteps. The preInitialState may differ from the initialState e.g. if the preInitialState has an eventless transition to a different state.

Comment on lines 29 to 32
if (this.index > this.events.length - 1) {
this.events.length = 0;
this.index = 0;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This strategy doesn't scale because we retain all the queued items up until we drain the queue. The items that were already processed should be immediately disposed because we don't have any use for them from that point in time.

I've reimplemented this mailbox using a simple linked list to avoid this problem altogether:
37547c3

return this;
}

private flush() {
this.mailbox.status = 'processing';
let event = this.mailbox.dequeue();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation doesn't scale too well with multiple consumers of the mailbox because a lot of the delicate queueing/dequeuing management becomes the responsibility of each consumer. I think I've managed to avoid this in this PR:
37547c3

With these changes, the consumer only needs to provide a "process" callback to the created mailbox and call simple start, clear and enqueue methods. Thanks to that I've been able to reuse easily this Mailbox implementation in other 2 places that were managing this:
00f1d74
e530b5b

@davidkpiano davidkpiano merged commit c34a858 into next Jan 10, 2022
@davidkpiano davidkpiano deleted the v5/mailbox branch January 10, 2022 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants