Skip to content

Commit

Permalink
Remove State['_internalQueue'] (#4372)
Browse files Browse the repository at this point in the history
* Remove `State['_internalQueue']`

* add a changeset
  • Loading branch information
Andarist committed Oct 19, 2023
1 parent e0733b4 commit c19e6fb
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 78 deletions.
5 changes: 5 additions & 0 deletions .changeset/proud-apricots-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': major
---

Removed `State['_internalQueue']`.
2 changes: 0 additions & 2 deletions packages/core/src/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ export class State<
public error: unknown;
public context: TContext;
public historyValue: Readonly<HistoryValue<TContext, TEvent>> = {};
public _internalQueue: Array<TEvent>;
/**
* The enabled state nodes representative of the state value.
*/
Expand Down Expand Up @@ -171,7 +170,6 @@ export class State<
public machine: AnyStateMachine
) {
this.context = config.context;
this._internalQueue = config._internalQueue ?? [];
this.historyValue = config.historyValue || {};
this.matches = this.matches.bind(this);
this.toStrings = this.toStrings.bind(this);
Expand Down
27 changes: 19 additions & 8 deletions packages/core/src/StateMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,8 @@ export class StateMachine<
*/
private getPreInitialState(
actorCtx: AnyActorContext,
initEvent: any
initEvent: any,
internalQueue: AnyEventObject[]
): MachineSnapshot<
TContext,
TEvent,
Expand All @@ -419,9 +420,13 @@ export class StateMachine<
if (typeof context === 'function') {
const assignment = ({ spawn, event }: any) =>
context({ spawn, input: event.input });
return resolveActionsAndContext(preInitial, initEvent, actorCtx, [
assign(assignment)
]) as SnapshotFrom<this>;
return resolveActionsAndContext(
preInitial,
initEvent,
actorCtx,
[assign(assignment)],
internalQueue
) as SnapshotFrom<this>;
}

return preInitial;
Expand Down Expand Up @@ -452,8 +457,12 @@ export class StateMachine<
TResolvedTypesMeta
> {
const initEvent = createInitEvent(input) as unknown as TEvent; // TODO: fix;

const preInitialState = this.getPreInitialState(actorCtx, initEvent);
const internalQueue: AnyEventObject[] = [];
const preInitialState = this.getPreInitialState(
actorCtx,
initEvent,
internalQueue
);
const nextState = microstep(
[
{
Expand All @@ -468,13 +477,15 @@ export class StateMachine<
preInitialState,
actorCtx,
initEvent,
true
true,
internalQueue
);

const { state: macroState } = macrostep(
nextState,
initEvent as AnyEventObject,
actorCtx
actorCtx,
internalQueue
);

return macroState as SnapshotFrom<this>;
Expand Down
19 changes: 8 additions & 11 deletions packages/core/src/actions/raise.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import isDevelopment from '#is-development';
import { cloneState } from '../State.ts';
import {
ActionArgs,
AnyActorContext,
Expand All @@ -11,7 +10,8 @@ import {
NoInfer,
RaiseActionOptions,
SendExpr,
ParameterizedObject
ParameterizedObject,
AnyEventObject
} from '../types.ts';

function resolveRaise(
Expand Down Expand Up @@ -43,7 +43,8 @@ function resolveRaise(
EventObject
>
| undefined;
}
},
{ internalQueue }: { internalQueue: AnyEventObject[] }
) {
const delaysMap = state.machine.implementations.delays;

Expand All @@ -63,14 +64,10 @@ function resolveRaise(
} else {
resolvedDelay = typeof delay === 'function' ? delay(args) : delay;
}
return [
typeof resolvedDelay !== 'number'
? cloneState(state, {
_internalQueue: state._internalQueue.concat(resolvedEvent)
})
: state,
{ event: resolvedEvent, id, delay: resolvedDelay }
];
if (typeof resolvedDelay !== 'number') {
internalQueue.push(resolvedEvent);
}
return [state, { event: resolvedEvent, id, delay: resolvedDelay }];
}

function executeRaise(
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/actions/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function resolveSendTo(
>
| undefined;
},
extra: { deferredActorIds: string[] } | undefined
extra: { deferredActorIds: string[] | undefined }
) {
const delaysMap = state.machine.implementations.delays;

Expand Down Expand Up @@ -95,7 +95,7 @@ function resolveSendTo(
// #_invokeid. If the target is the special term '#_invokeid', where invokeid is the invokeid of an SCXML session that the sending session has created by <invoke>, the Processor must add the event to the external queue of that session.
targetActorRef = state.children[resolvedTarget.slice(2)];
} else {
targetActorRef = extra?.deferredActorIds.includes(resolvedTarget)
targetActorRef = extra.deferredActorIds?.includes(resolvedTarget)
? resolvedTarget
: state.children[resolvedTarget];
}
Expand Down
103 changes: 57 additions & 46 deletions packages/core/src/stateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -974,26 +974,24 @@ export function microstep<
currentState: AnyState,
actorCtx: AnyActorContext,
event: TEvent,
isInitial: boolean
isInitial: boolean,
internalQueue: Array<AnyEventObject>
): AnyState {
const mutConfiguration = new Set(currentState.configuration);

if (!transitions.length) {
return currentState;
}

const microstate = microstepProcedure(
return microstepProcedure(
transitions,
currentState,
mutConfiguration,
event,
actorCtx,
isInitial
isInitial,
internalQueue
);

return cloneState(microstate, {
value: {} // TODO: make optional
});
}

function microstepProcedure(
Expand All @@ -1002,7 +1000,8 @@ function microstepProcedure(
mutConfiguration: Set<AnyStateNode>,
event: AnyEventObject,
actorCtx: AnyActorContext,
isInitial: boolean
isInitial: boolean,
internalQueue: Array<AnyEventObject>
): typeof currentState {
const historyValue = {
...currentState.historyValue
Expand All @@ -1014,12 +1013,7 @@ function microstepProcedure(
historyValue
);

const internalQueue = [...currentState._internalQueue];
// TODO: this `cloneState` is really just a hack to prevent infinite loops
// we need to take another look at how internal queue is managed
let nextState = cloneState(currentState, {
_internalQueue: []
});
let nextState = currentState;

// Exit states
if (!isInitial) {
Expand All @@ -1029,7 +1023,8 @@ function microstepProcedure(
actorCtx,
filteredTransitions,
mutConfiguration,
historyValue
historyValue,
internalQueue
);
}

Expand All @@ -1038,7 +1033,8 @@ function microstepProcedure(
nextState,
event,
actorCtx,
filteredTransitions.flatMap((t) => t.actions)
filteredTransitions.flatMap((t) => t.actions),
internalQueue
);

// Enter states
Expand All @@ -1062,17 +1058,15 @@ function microstepProcedure(
actorCtx,
nextConfiguration
.sort((a, b) => b.order - a.order)
.flatMap((state) => state.exit)
.flatMap((state) => state.exit),
internalQueue
);
}

try {
internalQueue.push(...nextState._internalQueue);

return cloneState(nextState, {
configuration: nextConfiguration,
historyValue,
_internalQueue: internalQueue
historyValue
});
} catch (e) {
// TODO: Refactor this once proper error handling is implemented.
Expand Down Expand Up @@ -1160,6 +1154,7 @@ function enterStates(
event,
actorCtx,
actions,
internalQueue,
stateNodeToEnter.invoke.map((invokeDef) => invokeDef.id)
);

Expand Down Expand Up @@ -1374,7 +1369,8 @@ function exitStates(
actorCtx: AnyActorContext,
transitions: AnyTransitionDefinition[],
mutConfiguration: Set<AnyStateNode>,
historyValue: HistoryValue<any, any>
historyValue: HistoryValue<any, any>,
internalQueue: AnyEventObject[]
) {
let nextState = currentState;
const statesToExit = computeExitSet(
Expand Down Expand Up @@ -1403,10 +1399,13 @@ function exitStates(
}

for (const s of statesToExit) {
nextState = resolveActionsAndContext(nextState, event, actorCtx, [
...s.exit,
...s.invoke.map((def) => stop(def.id))
]);
nextState = resolveActionsAndContext(
nextState,
event,
actorCtx,
[...s.exit, ...s.invoke.map((def) => stop(def.id))],
internalQueue
);
mutConfiguration.delete(s);
}
return nextState;
Expand Down Expand Up @@ -1434,7 +1433,10 @@ function resolveActionsAndContextWorker(
event: AnyEventObject,
actorCtx: AnyActorContext,
actions: UnknownAction[],
extra: { deferredActorIds: string[] } | undefined,
extra: {
internalQueue: AnyEventObject[];
deferredActorIds: string[] | undefined;
},
retries: (readonly [BuiltinAction, unknown])[] | undefined
): AnyState {
const { machine } = currentState;
Expand Down Expand Up @@ -1539,6 +1541,7 @@ export function resolveActionsAndContext(
event: AnyEventObject,
actorCtx: AnyActorContext,
actions: UnknownAction[],
internalQueue: AnyEventObject[],
deferredActorIds?: string[]
): AnyState {
const retries: (readonly [BuiltinAction, unknown])[] | undefined =
Expand All @@ -1548,7 +1551,7 @@ export function resolveActionsAndContext(
event,
actorCtx,
actions,
deferredActorIds && { deferredActorIds },
{ internalQueue, deferredActorIds },
retries
);
retries?.forEach(([builtinAction, params]) => {
Expand All @@ -1560,7 +1563,8 @@ export function resolveActionsAndContext(
export function macrostep(
state: AnyState,
event: EventObject,
actorCtx: AnyActorContext
actorCtx: AnyActorContext,
internalQueue: AnyEventObject[] = []
): {
state: typeof state;
microstates: Array<typeof state>;
Expand Down Expand Up @@ -1589,37 +1593,44 @@ export function macrostep(
// Determine the next state based on the next microstep
if (nextEvent.type !== XSTATE_INIT) {
const transitions = selectTransitions(nextEvent, nextState);
nextState = microstep(transitions, state, actorCtx, nextEvent, false);
nextState = microstep(
transitions,
state,
actorCtx,
nextEvent,
false,
internalQueue
);
states.push(nextState);
}

while (nextState.status === 'active') {
let enabledTransitions = selectEventlessTransitions(nextState, nextEvent);

if (!enabledTransitions.length) {
if (!nextState._internalQueue.length) {
if (!internalQueue.length) {
break;
} else {
nextEvent = nextState._internalQueue[0];
const transitions = selectTransitions(nextEvent, nextState);
nextState = microstep(
transitions,
nextState,
actorCtx,
nextEvent,
false
);
nextState._internalQueue.shift();

states.push(nextState);
}
nextEvent = internalQueue.shift()!;
const transitions = selectTransitions(nextEvent, nextState);
nextState = microstep(
transitions,
nextState,
actorCtx,
nextEvent,
false,
internalQueue
);

states.push(nextState);
} else {
nextState = microstep(
enabledTransitions,
nextState,
actorCtx,
nextEvent,
false
false,
internalQueue
);

states.push(nextState);
Expand Down Expand Up @@ -1654,7 +1665,7 @@ function stopStep(
actions.push(stop(child));
}

return resolveActionsAndContext(nextState, event, actorCtx, actions);
return resolveActionsAndContext(nextState, event, actorCtx, actions, []);
}

function selectTransitions(
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,6 @@ export interface StateConfig<
error?: unknown;
tags?: Set<string>;
machine?: StateMachine<TContext, TEvent, any, any, any, any, any, any, any>;
_internalQueue?: Array<TEvent>;
}

export interface ActorOptions<TLogic extends AnyActorLogic> {
Expand Down
Loading

0 comments on commit c19e6fb

Please sign in to comment.