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

[core] Add .getSnapshot() to actor refs #2197

Merged
merged 3 commits into from
May 20, 2021
Merged

Conversation

davidkpiano
Copy link
Member

@davidkpiano davidkpiano commented May 14, 2021

This PR adds the .getSnapshot() method, which allows you to synchronously retrieve the latest value emitted from that actor. That value may be undefined if no value has been emitted yet.

This is the first step in full actor persistence, since persisting the state of a machine at a specific point in time involves synchronously reading the "state" (really the last known emitted values) of each actor.

const machine = createMachine({
  context: {
    promiseRef: null
  },
  initial: 'pending',
  states: {
    pending: {
      entry: assign({
        promiseRef: () => spawn(fetch(/* ... */), 'some-promise')
      })
    }
  }
});

const service = interpret(machine)
  .onTransition((state) => {
    // Read promise value synchronously
    const resolvedValue = state.context.promiseRef?.getSnapshot();
    // => undefined (if promise not resolved yet)
    // => { ... } (resolved data)
  })
  .start();

// ...

@changeset-bot
Copy link

changeset-bot bot commented May 14, 2021

🦋 Changeset detected

Latest commit: 1432ae7

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

This PR includes changesets to release 1 package
Name Type
xstate Minor

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

Comment on lines 1163 to 1166
const subscription = source.subscribe(
(value) => {
emitted = value;
this.send(toSCXMLEvent(value, { origin: id }));
Copy link
Member

Choose a reason for hiding this comment

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

I don't like how this uses a single value for 2 distinct concepts in case of observables. Currently, observables must emit something that extends TEvent and that gets automatically~ sent to the parent as the event. Persisting that as a snapshot seems weird but I also fail to think of a better design around observables that would handle the conversion to actor interface automatically.

Note that the same happens for callbacks but when it comes to machines and promises both things are separate.

Copy link
Member Author

Choose a reason for hiding this comment

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

For invoked observables, yes, the emitted values are the same as events sent back to the parent, which is why I thought it was fine to have this be the snapshot value.

If we did it a different way, we would have to generate events that look like:

{
  type: "???",
  data: {/* emitted data */}
}

But what would the event.type be? (maybe more of a v5 question)

Copy link
Member

Choose a reason for hiding this comment

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

That would have to wrap all the events sent back to the parent (otherwise we'd end up with some magic blackbox algorithm for choosing what should be wrapped and what should not be). And that would in-turn prevent sending events of different types back to the parent from observables and also prevent implementing "epics" (observables fed with events by the machine and responding with events to the machine).

So maybe what you have done here is best? or maybe it should just be undefined? Or maybe we should have to types for observables in the future? After all, subjects (for which this makes sense as what they emit is usually state-like) are somewhat different from other observables (which more often than not emit one-time events without expectation of it being persisted as it's only relevant at that point in time). OTOH the latter type is often built using stateful/aggregating operators in the middle so that state is relevant for them but OYAH we can't rehydrate operators "in the middle" anyway.

@NixBiks
Copy link

NixBiks commented May 18, 2021

Is this planned for v4 as well?

@davidkpiano
Copy link
Member Author

Is this planned for v4 as well?

This PR is targeted for v4 specifically! Of course, it will make its way into v5 shortly thereafter.

@davydof
Copy link

davydof commented May 18, 2021

@davidkpiano
if you can please merge this PR ASAP - I need it to implement feature with store nested machines

@NixBiks
Copy link

NixBiks commented May 18, 2021

At the moment I have a machine that uses sendUpdate to make sure the parent get the latest updates from the child actor machine. The problem is that I can't create the child machine without a parent then (which I would like). With getSnapshot can I then use the child machine with and without a parent (since the child doesn't really know about getSnapshot if not called)?

@davidkpiano
Copy link
Member Author

At the moment I have a machine that uses sendUpdate to make sure the parent get the latest updates from the child actor machine. The problem is that I can't create the child machine without a parent then (which I would like). With getSnapshot can I then use the child machine with and without a parent (since the child doesn't really know about getSnapshot if not called)?

Yes you can, if I understand your use-case correctly. Feel free to share code if it makes it more clear.

@davidkpiano
Copy link
Member Author

@Andarist Is this good to merge as-is?

We can still adjust for different use-cases (e.g., observable snapshots, etc.) for v5.

@davidkpiano davidkpiano merged commit a4256dc into main May 20, 2021
This was referenced May 20, 2021
@Andarist Andarist deleted the davidkpiano/getsnapshot branch March 22, 2023 15:03
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

4 participants