use eventQueue; process Reactions in proper order
This commit is contained in:
Vendored
+50
-38
@@ -3,62 +3,74 @@
|
|||||||
var Machine = function(...states) {
|
var Machine = function(...states) {
|
||||||
return { states };
|
return { states };
|
||||||
};
|
};
|
||||||
var State = function(name, ...ons) {
|
var State = function(name, ...eventReactionCouplings) {
|
||||||
return { name, ons };
|
return { name, eventReactionCouplings };
|
||||||
};
|
};
|
||||||
var On = function(eventName, ...reactions) {
|
var On = function(eventName, ...reactions) {
|
||||||
return { eventName, reactions };
|
return { eventName, reactions };
|
||||||
};
|
};
|
||||||
var Do = function(fn) {
|
var SideEffect = function(fn) {
|
||||||
return { type: "Do", fn };
|
return { type: "SideEffect", fn };
|
||||||
};
|
};
|
||||||
var Goto = function(targetStateName) {
|
var Goto = function(targetStateName) {
|
||||||
return { type: "Goto", targetStateName };
|
return { type: "Goto", targetStateName };
|
||||||
};
|
};
|
||||||
var Tick = function(doFunctions) {
|
|
||||||
return { doFunctions };
|
|
||||||
};
|
|
||||||
function interpret(machine2, options) {
|
function interpret(machine2, options) {
|
||||||
let { state, context } = options;
|
let { state, context } = options;
|
||||||
if (typeof state === "undefined") {
|
if (typeof state === "undefined") {
|
||||||
state = machine2.states[0].name;
|
state = machine2.states[0].name;
|
||||||
}
|
}
|
||||||
const interpreter = { machine: machine2, state, context, tickQueues: [] };
|
const interpreter = { machine: machine2, state, context, eventQueue: [], isTransitioning: false };
|
||||||
console.log(interpreter);
|
|
||||||
send(interpreter, ["entry", null]);
|
send(interpreter, ["entry", null]);
|
||||||
return interpreter;
|
return interpreter;
|
||||||
}
|
}
|
||||||
function getState(interpreter) {
|
function getState(interpreter) {
|
||||||
return interpreter.machine.states.find((state) => state.name === interpreter.state);
|
return interpreter.machine.states.find((state) => state.name === interpreter.state);
|
||||||
}
|
}
|
||||||
function getOns(state, event) {
|
function getMatchingEventReactionCouplings(state, event) {
|
||||||
return state.ons.filter((on) => on.eventName === event[0]);
|
return state.eventReactionCouplings.filter((eventReactionCoupling) => eventReactionCoupling.eventName === event[0]);
|
||||||
}
|
|
||||||
function noop() {
|
|
||||||
}
|
}
|
||||||
function send(interpreter, event) {
|
function send(interpreter, event) {
|
||||||
const state = getState(interpreter);
|
interpreter.eventQueue.push(event);
|
||||||
const onsTree = getOns(state, event);
|
if (interpreter.isTransitioning === false) {
|
||||||
const reactions = onsTree.map((on) => on.reactions).flat();
|
interpreter.isTransitioning = true;
|
||||||
const indexOfFirstGoto = reactions.findIndex((reaction) => reaction.type === "Goto");
|
while (interpreter.eventQueue.length > 0) {
|
||||||
const indexOfFinalReaction = indexOfFirstGoto === -1 ? reactions.length - 1 : indexOfFirstGoto;
|
processNextEvent(interpreter);
|
||||||
const reactionsUntilFirstGoto = reactions.slice(0, indexOfFinalReaction + 1);
|
}
|
||||||
const functionsToRunInTick = reactionsUntilFirstGoto.map((reaction) => {
|
interpreter.isTransitioning = false;
|
||||||
if (reaction.type === "Do") {
|
}
|
||||||
return reaction.fn;
|
}
|
||||||
|
function processNextEvent(interpreter) {
|
||||||
|
const nextEvent = interpreter.eventQueue.shift();
|
||||||
|
if (typeof nextEvent !== "undefined") {
|
||||||
|
const state = getState(interpreter);
|
||||||
|
const eventReactionCouplings = getMatchingEventReactionCouplings(state, nextEvent);
|
||||||
|
const reactions = eventReactionCouplings.map((eventReactionCoupling) => eventReactionCoupling.reactions).flat();
|
||||||
|
const { sideEffects, contextMutations, goto_ } = categorizeReactions(reactions);
|
||||||
|
sideEffects.forEach((sideEffect) => {
|
||||||
|
sideEffect.fn(interpreter.context, nextEvent, interpreter);
|
||||||
|
});
|
||||||
|
contextMutations.forEach((contextMutation) => {
|
||||||
|
interpreter.context = contextMutation.fn(interpreter.context, nextEvent, interpreter);
|
||||||
|
});
|
||||||
|
if (goto_ !== null) {
|
||||||
|
interpreter.state = goto_.targetStateName;
|
||||||
|
send(interpreter, ["entry", null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function categorizeReactions(reactions) {
|
||||||
|
let sideEffects = [], contextMutations = [], goto_ = null;
|
||||||
|
reactions.forEach((reaction) => {
|
||||||
|
if (reaction.type === "SideEffect") {
|
||||||
|
sideEffects.push(reaction);
|
||||||
|
} else if (reaction.type === "ContextMutation") {
|
||||||
|
contextMutations.push(reaction);
|
||||||
} else if (reaction.type === "Goto") {
|
} else if (reaction.type === "Goto") {
|
||||||
return (ctx, e, self) => {
|
goto_ = reaction;
|
||||||
self.state = reaction.targetStateName;
|
|
||||||
send(self, ["entry", e]);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return noop;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const tick = Tick(functionsToRunInTick);
|
return { sideEffects, contextMutations, goto_ };
|
||||||
tick.doFunctions.forEach((fn) => {
|
|
||||||
fn(interpreter.context, event, interpreter);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/tests/00-basic.ts
|
// src/tests/00-basic.ts
|
||||||
@@ -75,8 +87,8 @@
|
|||||||
"green",
|
"green",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(beginTimer),
|
SideEffect(beginTimer),
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"timer-finished",
|
"timer-finished",
|
||||||
@@ -87,8 +99,8 @@
|
|||||||
"yellow",
|
"yellow",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(beginTimer),
|
SideEffect(beginTimer),
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"timer-finished",
|
"timer-finished",
|
||||||
@@ -99,8 +111,8 @@
|
|||||||
"red",
|
"red",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(beginTimer),
|
SideEffect(beginTimer),
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"timer-finished",
|
"timer-finished",
|
||||||
|
|||||||
Vendored
+55
-43
@@ -3,62 +3,74 @@
|
|||||||
var Machine = function(...states) {
|
var Machine = function(...states) {
|
||||||
return { states };
|
return { states };
|
||||||
};
|
};
|
||||||
var State = function(name, ...ons) {
|
var State = function(name, ...eventReactionCouplings) {
|
||||||
return { name, ons };
|
return { name, eventReactionCouplings };
|
||||||
};
|
};
|
||||||
var On = function(eventName, ...reactions) {
|
var On = function(eventName, ...reactions) {
|
||||||
return { eventName, reactions };
|
return { eventName, reactions };
|
||||||
};
|
};
|
||||||
var Do = function(fn) {
|
var SideEffect = function(fn) {
|
||||||
return { type: "Do", fn };
|
return { type: "SideEffect", fn };
|
||||||
};
|
};
|
||||||
var Goto = function(targetStateName) {
|
var Goto = function(targetStateName) {
|
||||||
return { type: "Goto", targetStateName };
|
return { type: "Goto", targetStateName };
|
||||||
};
|
};
|
||||||
var Tick = function(doFunctions) {
|
|
||||||
return { doFunctions };
|
|
||||||
};
|
|
||||||
function interpret(machine, options) {
|
function interpret(machine, options) {
|
||||||
let { state, context } = options;
|
let { state, context } = options;
|
||||||
if (typeof state === "undefined") {
|
if (typeof state === "undefined") {
|
||||||
state = machine.states[0].name;
|
state = machine.states[0].name;
|
||||||
}
|
}
|
||||||
const interpreter = { machine, state, context, tickQueues: [] };
|
const interpreter = { machine, state, context, eventQueue: [], isTransitioning: false };
|
||||||
console.log(interpreter);
|
|
||||||
send(interpreter, ["entry", null]);
|
send(interpreter, ["entry", null]);
|
||||||
return interpreter;
|
return interpreter;
|
||||||
}
|
}
|
||||||
function getState(interpreter) {
|
function getState(interpreter) {
|
||||||
return interpreter.machine.states.find((state) => state.name === interpreter.state);
|
return interpreter.machine.states.find((state) => state.name === interpreter.state);
|
||||||
}
|
}
|
||||||
function getOns(state, event) {
|
function getMatchingEventReactionCouplings(state, event) {
|
||||||
return state.ons.filter((on) => on.eventName === event[0]);
|
return state.eventReactionCouplings.filter((eventReactionCoupling) => eventReactionCoupling.eventName === event[0]);
|
||||||
}
|
|
||||||
function noop() {
|
|
||||||
}
|
}
|
||||||
function send(interpreter, event) {
|
function send(interpreter, event) {
|
||||||
const state = getState(interpreter);
|
interpreter.eventQueue.push(event);
|
||||||
const onsTree = getOns(state, event);
|
if (interpreter.isTransitioning === false) {
|
||||||
const reactions = onsTree.map((on) => on.reactions).flat();
|
interpreter.isTransitioning = true;
|
||||||
const indexOfFirstGoto = reactions.findIndex((reaction) => reaction.type === "Goto");
|
while (interpreter.eventQueue.length > 0) {
|
||||||
const indexOfFinalReaction = indexOfFirstGoto === -1 ? reactions.length - 1 : indexOfFirstGoto;
|
processNextEvent(interpreter);
|
||||||
const reactionsUntilFirstGoto = reactions.slice(0, indexOfFinalReaction + 1);
|
}
|
||||||
const functionsToRunInTick = reactionsUntilFirstGoto.map((reaction) => {
|
interpreter.isTransitioning = false;
|
||||||
if (reaction.type === "Do") {
|
}
|
||||||
return reaction.fn;
|
}
|
||||||
|
function processNextEvent(interpreter) {
|
||||||
|
const nextEvent = interpreter.eventQueue.shift();
|
||||||
|
if (typeof nextEvent !== "undefined") {
|
||||||
|
const state = getState(interpreter);
|
||||||
|
const eventReactionCouplings = getMatchingEventReactionCouplings(state, nextEvent);
|
||||||
|
const reactions = eventReactionCouplings.map((eventReactionCoupling) => eventReactionCoupling.reactions).flat();
|
||||||
|
const { sideEffects, contextMutations, goto_ } = categorizeReactions(reactions);
|
||||||
|
sideEffects.forEach((sideEffect) => {
|
||||||
|
sideEffect.fn(interpreter.context, nextEvent, interpreter);
|
||||||
|
});
|
||||||
|
contextMutations.forEach((contextMutation) => {
|
||||||
|
interpreter.context = contextMutation.fn(interpreter.context, nextEvent, interpreter);
|
||||||
|
});
|
||||||
|
if (goto_ !== null) {
|
||||||
|
interpreter.state = goto_.targetStateName;
|
||||||
|
send(interpreter, ["entry", null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function categorizeReactions(reactions) {
|
||||||
|
let sideEffects = [], contextMutations = [], goto_ = null;
|
||||||
|
reactions.forEach((reaction) => {
|
||||||
|
if (reaction.type === "SideEffect") {
|
||||||
|
sideEffects.push(reaction);
|
||||||
|
} else if (reaction.type === "ContextMutation") {
|
||||||
|
contextMutations.push(reaction);
|
||||||
} else if (reaction.type === "Goto") {
|
} else if (reaction.type === "Goto") {
|
||||||
return (ctx, e, self) => {
|
goto_ = reaction;
|
||||||
self.state = reaction.targetStateName;
|
|
||||||
send(self, ["entry", e]);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return noop;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const tick = Tick(functionsToRunInTick);
|
return { sideEffects, contextMutations, goto_ };
|
||||||
tick.doFunctions.forEach((fn) => {
|
|
||||||
fn(interpreter.context, event, interpreter);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// src/tests/01-ping-pong.ts
|
// src/tests/01-ping-pong.ts
|
||||||
@@ -85,11 +97,11 @@
|
|||||||
"idle",
|
"idle",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"server-created",
|
"server-created",
|
||||||
Do((_ctx, [_eventName, serverActor2], self) => {
|
SideEffect((_ctx, [_eventName, serverActor2], self) => {
|
||||||
self.context.serverActor = serverActor2;
|
self.context.serverActor = serverActor2;
|
||||||
}),
|
}),
|
||||||
Goto("making-request")
|
Goto("making-request")
|
||||||
@@ -99,8 +111,8 @@
|
|||||||
"making-request",
|
"making-request",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
Do(makeRequest),
|
SideEffect(makeRequest),
|
||||||
Goto("awaiting-response")
|
Goto("awaiting-response")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -108,11 +120,11 @@
|
|||||||
"awaiting-response",
|
"awaiting-response",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"received-response",
|
"received-response",
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
Goto("making-request")
|
Goto("making-request")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -122,11 +134,11 @@
|
|||||||
"awaiting-request",
|
"awaiting-request",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"received-request",
|
"received-request",
|
||||||
Do((_ctx, [_eventName, clientActor2], self) => {
|
SideEffect((_ctx, [_eventName, clientActor2], self) => {
|
||||||
self.context.clientActor = clientActor2;
|
self.context.clientActor = clientActor2;
|
||||||
}),
|
}),
|
||||||
Goto("sending-response")
|
Goto("sending-response")
|
||||||
@@ -136,12 +148,12 @@
|
|||||||
"sending-response",
|
"sending-response",
|
||||||
On(
|
On(
|
||||||
"entry",
|
"entry",
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
Do(startTimer)
|
SideEffect(startTimer)
|
||||||
),
|
),
|
||||||
On(
|
On(
|
||||||
"timer-finished",
|
"timer-finished",
|
||||||
Do(sendResponse),
|
SideEffect(sendResponse),
|
||||||
Goto("awaiting-request")
|
Goto("awaiting-request")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+74
-49
@@ -4,47 +4,50 @@ export interface Machine_T {
|
|||||||
}
|
}
|
||||||
export interface State_T {
|
export interface State_T {
|
||||||
name: string;
|
name: string;
|
||||||
ons: Array<On_T>;
|
eventReactionCouplings: Array<EventReactionCouplings_T>;
|
||||||
}
|
}
|
||||||
export interface On_T {
|
export interface EventReactionCouplings_T {
|
||||||
eventName: string;
|
eventName: string;
|
||||||
reactions: Array<Do_T | Goto_T>;
|
reactions: Array<Reaction_T>;
|
||||||
};
|
};
|
||||||
export interface Do_T {
|
export type Reaction_T = SideEffect_T | ContextMutation_T | Goto_T;
|
||||||
type: 'Do';
|
export interface SideEffect_T {
|
||||||
fn: DoFn_T;
|
type: 'SideEffect';
|
||||||
|
fn: SideEffectFunction_T;
|
||||||
};
|
};
|
||||||
export type DoFn_T = (ctx:any,e:Event_T,self:Interpreter_T)=>void;
|
export type SideEffectFunction_T = (ctx:any,e:Event_T,self:Interpreter_T)=>void;
|
||||||
|
export interface ContextMutation_T {
|
||||||
|
type: 'ContextMutation';
|
||||||
|
fn: ContextMutationFunction_T;
|
||||||
|
};
|
||||||
|
export type ContextMutationFunction_T = (ctx:any,e:Event_T,self:Interpreter_T)=>any;
|
||||||
export interface Goto_T {
|
export interface Goto_T {
|
||||||
type: 'Goto';
|
type: 'Goto';
|
||||||
targetStateName: string;
|
targetStateName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Machine = function(...states:Array<State_T>) : Machine_T { return {states}; };
|
export const Machine = function(...states:Array<State_T>) : Machine_T { return {states}; };
|
||||||
export const State = function(name:string, ...ons:Array<On_T>) : State_T{ return {name, ons}; };
|
export const State = function(name:string, ...eventReactionCouplings:Array<EventReactionCouplings_T>) : State_T{ return {name, eventReactionCouplings}; };
|
||||||
export const On = function(eventName:string, ...reactions:Array<Do_T | Goto_T>) : On_T{ return {eventName, reactions}; };
|
export const On = function(eventName:string, ...reactions:Array<Reaction_T>) : EventReactionCouplings_T{ return {eventName, reactions}; };
|
||||||
export const Do = function<S,E extends Event_T, C>(fn:DoFn_T) : Do_T{ return {type:'Do', fn}; };
|
export const SideEffect = function(fn:SideEffectFunction_T) : SideEffect_T{ return {type:'SideEffect', fn}; };
|
||||||
export const Goto = function(targetStateName:string) : Goto_T { return {type:'Goto', targetStateName} };
|
export const Goto = function(targetStateName:string) : Goto_T { return {type:'Goto', targetStateName} };
|
||||||
|
export const Context = function(fn:ContextMutationFunction_T) : ContextMutation_T { return {type:'ContextMutation', fn} };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface Tick_T {
|
|
||||||
doFunctions: Array<DoFn_T>
|
|
||||||
};
|
|
||||||
const Tick = function(doFunctions : Array<DoFn_T>) : Tick_T{ return {doFunctions}; };
|
|
||||||
|
|
||||||
|
|
||||||
export interface Interpreter_T {
|
export interface Interpreter_T {
|
||||||
machine: Machine_T;
|
machine: Machine_T;
|
||||||
state: string;
|
state: string;
|
||||||
context: any;
|
context: any;
|
||||||
tickQueues:Array<TickQueue_T>
|
eventQueue:Array<Event_T>;
|
||||||
|
isTransitioning: boolean;
|
||||||
}
|
}
|
||||||
type TickQueue_T = Array<Tick_T>;
|
|
||||||
export function interpret(machine:Machine_T, options:{state?:string, context:any}) : Interpreter_T{
|
export function interpret(machine:Machine_T, options:{state?:string, context:any}) : Interpreter_T{
|
||||||
let {state, context} = options;
|
let {state, context} = options;
|
||||||
if(typeof state === 'undefined'){ state = machine.states[0].name; }
|
if(typeof state === 'undefined'){ state = machine.states[0].name; }
|
||||||
const interpreter = {machine, state, context, tickQueues:[]}
|
const interpreter = {machine, state, context, eventQueue:[], isTransitioning:false}
|
||||||
console.log(interpreter);
|
|
||||||
//@ts-ignore
|
|
||||||
send(interpreter, ['entry', null] );
|
send(interpreter, ['entry', null] );
|
||||||
return interpreter;
|
return interpreter;
|
||||||
}
|
}
|
||||||
@@ -56,12 +59,9 @@ function getState(interpreter : Interpreter_T) : State_T{
|
|||||||
}
|
}
|
||||||
/** Helper function for `send()`
|
/** Helper function for `send()`
|
||||||
*/
|
*/
|
||||||
function getOns(state : State_T, event:Event_T) : Array<On_T>{
|
function getMatchingEventReactionCouplings(state : State_T, event:Event_T) : Array<EventReactionCouplings_T>{
|
||||||
return state.ons.filter((on)=>on.eventName===event[0]);
|
return state.eventReactionCouplings.filter((eventReactionCoupling)=>eventReactionCoupling.eventName===event[0]);
|
||||||
}
|
}
|
||||||
/** Helper function for `send()`
|
|
||||||
*/
|
|
||||||
function noop(){}
|
|
||||||
/** Inject an Event into the Interpreter's "tick queue".
|
/** Inject an Event into the Interpreter's "tick queue".
|
||||||
*
|
*
|
||||||
* An event can be signify something "new" happening, such that its reactions should run on the next Tick;
|
* An event can be signify something "new" happening, such that its reactions should run on the next Tick;
|
||||||
@@ -73,32 +73,57 @@ function noop(){}
|
|||||||
* Tick, it is discarded.
|
* Tick, it is discarded.
|
||||||
*/
|
*/
|
||||||
export function send(interpreter : Interpreter_T, event:Event_T){
|
export function send(interpreter : Interpreter_T, event:Event_T){
|
||||||
const state = getState(interpreter);
|
interpreter.eventQueue.push(event);
|
||||||
const onsTree = getOns(state, event);
|
if(interpreter.isTransitioning === false){
|
||||||
const reactions = onsTree
|
interpreter.isTransitioning = true;
|
||||||
.map((on)=>on.reactions)
|
while(interpreter.eventQueue.length > 0){
|
||||||
.flat();
|
processNextEvent(interpreter);
|
||||||
const indexOfFirstGoto = reactions.findIndex((reaction)=>reaction.type==='Goto');
|
}
|
||||||
const indexOfFinalReaction = indexOfFirstGoto === -1 ? reactions.length-1 : indexOfFirstGoto;
|
interpreter.isTransitioning = false;
|
||||||
const reactionsUntilFirstGoto = reactions.slice(0, indexOfFinalReaction+1);
|
}
|
||||||
const functionsToRunInTick = reactionsUntilFirstGoto
|
}
|
||||||
.map((reaction)=>{
|
export const enqueue = send;
|
||||||
if(reaction.type === 'Do'){
|
function processNextEvent(interpreter:Interpreter_T){
|
||||||
return reaction.fn;
|
const nextEvent = interpreter.eventQueue.shift();
|
||||||
}
|
if(typeof nextEvent !== 'undefined'){
|
||||||
else if(reaction.type === 'Goto'){
|
const state = getState(interpreter);
|
||||||
return (ctx:any,e:Event_T,self:Interpreter_T)=>{
|
const eventReactionCouplings = getMatchingEventReactionCouplings(state, nextEvent);
|
||||||
self.state = reaction.targetStateName;
|
const reactions = eventReactionCouplings
|
||||||
//@ts-ignore
|
.map((eventReactionCoupling)=>eventReactionCoupling.reactions)
|
||||||
send(self, ['entry', e] );
|
.flat();
|
||||||
};
|
const {sideEffects, contextMutations, goto_} = categorizeReactions(reactions);
|
||||||
}
|
// can process sideEffects in parallel:
|
||||||
else{
|
sideEffects.forEach((sideEffect)=>{
|
||||||
return noop;
|
sideEffect.fn(interpreter.context, nextEvent, interpreter);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
const tick = Tick(functionsToRunInTick);
|
// must process contextMutations in-series:
|
||||||
tick.doFunctions.forEach((fn)=>{ fn(interpreter.context, event, interpreter); });
|
contextMutations.forEach((contextMutation)=>{
|
||||||
|
interpreter.context = contextMutation.fn(interpreter.context, nextEvent, interpreter);
|
||||||
|
});
|
||||||
|
// processing of `goto` must be last:
|
||||||
|
if(goto_ !== null){
|
||||||
|
interpreter.state = goto_.targetStateName;
|
||||||
|
send(interpreter, ['entry', null]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function categorizeReactions(reactions:Array<Reaction_T>) : {sideEffects:Array<SideEffect_T>, contextMutations:Array<ContextMutation_T>, goto_:Goto_T|null}{
|
||||||
|
let
|
||||||
|
sideEffects:Array<SideEffect_T> = [],
|
||||||
|
contextMutations:Array<ContextMutation_T> = [],
|
||||||
|
goto_:Goto_T|null = null;
|
||||||
|
reactions.forEach((reaction)=>{
|
||||||
|
if(reaction.type === 'SideEffect'){
|
||||||
|
sideEffects.push(reaction);
|
||||||
|
}
|
||||||
|
else if(reaction.type === 'ContextMutation'){
|
||||||
|
contextMutations.push(reaction);
|
||||||
|
}
|
||||||
|
else if(reaction.type === 'Goto'){
|
||||||
|
goto_ = reaction;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {sideEffects, contextMutations, goto_};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Spawn = function(){};
|
export const Spawn = function(){};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Machine, State, On, Do, Goto, Spawn, Unspawn, interpret, Interpreter_T, send, Event_T } from '../index';
|
import { Machine, State, On, SideEffect, Goto, Spawn, Unspawn, interpret, Interpreter_T, send, Event_T } from '../index';
|
||||||
|
|
||||||
const beginTimer = (ctx:C, e:Event_T, self:Interpreter_T)=>{ setTimeout(()=>{ send(self, ['timer-finished',null]); }, 800); };
|
const beginTimer = (ctx:C, e:Event_T, self:Interpreter_T)=>{ setTimeout(()=>{ send(self, ['timer-finished',null]); }, 800); };
|
||||||
const log = (ctx:C, e:Event_T, self:Interpreter_T)=>{ console.log(self.state); };
|
const log = (ctx:C, e:Event_T, self:Interpreter_T)=>{ console.log(self.state); };
|
||||||
@@ -16,8 +16,8 @@ const machine =
|
|||||||
Machine(
|
Machine(
|
||||||
State('green',
|
State('green',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(beginTimer),
|
SideEffect(beginTimer),
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On('timer-finished',
|
On('timer-finished',
|
||||||
Goto('yellow')
|
Goto('yellow')
|
||||||
@@ -25,8 +25,8 @@ const machine =
|
|||||||
),
|
),
|
||||||
State('yellow',
|
State('yellow',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(beginTimer),
|
SideEffect(beginTimer),
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On('timer-finished',
|
On('timer-finished',
|
||||||
Goto('red')
|
Goto('red')
|
||||||
@@ -34,8 +34,8 @@ const machine =
|
|||||||
),
|
),
|
||||||
State('red',
|
State('red',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(beginTimer),
|
SideEffect(beginTimer),
|
||||||
Do(log)
|
SideEffect(log)
|
||||||
),
|
),
|
||||||
On('timer-finished',
|
On('timer-finished',
|
||||||
Goto('green')
|
Goto('green')
|
||||||
|
|||||||
+12
-13
@@ -1,4 +1,4 @@
|
|||||||
import { Machine, State, On, Do, Goto, Spawn, Unspawn, interpret, Interpreter_T, send, Event_T } from '../index';
|
import { Machine, State, On, SideEffect, Goto, Spawn, Unspawn, interpret, Interpreter_T, send, Event_T } from '../index';
|
||||||
|
|
||||||
const wait = (ms:number)=>new Promise((resolve)=>{ setTimeout(()=>{ resolve(1); }, ms); });
|
const wait = (ms:number)=>new Promise((resolve)=>{ setTimeout(()=>{ resolve(1); }, ms); });
|
||||||
const makeRequest = (ctx,e,self)=>{ send(ctx.serverActor, ['received-request',self]); };
|
const makeRequest = (ctx,e,self)=>{ send(ctx.serverActor, ['received-request',self]); };
|
||||||
@@ -21,27 +21,26 @@ const client =
|
|||||||
Machine(
|
Machine(
|
||||||
State('idle',
|
State('idle',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
),
|
),
|
||||||
On('server-created',
|
On('server-created',
|
||||||
|
SideEffect((_ctx,[_eventName,serverActor],self)=>{ self.context.serverActor=serverActor; }),
|
||||||
Do((_ctx,[_eventName,serverActor],self)=>{ self.context.serverActor=serverActor; }),
|
|
||||||
Goto('making-request')
|
Goto('making-request')
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
State('making-request',
|
State('making-request',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
Do(makeRequest),
|
SideEffect(makeRequest),
|
||||||
Goto('awaiting-response')
|
Goto('awaiting-response')
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
State('awaiting-response',
|
State('awaiting-response',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
),
|
),
|
||||||
On('received-response',
|
On('received-response',
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
Goto('making-request')
|
Goto('making-request')
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -60,20 +59,20 @@ const server =
|
|||||||
Machine(
|
Machine(
|
||||||
State('awaiting-request',
|
State('awaiting-request',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
),
|
),
|
||||||
On('received-request',
|
On('received-request',
|
||||||
Do((_ctx,[_eventName,clientActor],self)=>{ self.context.clientActor=clientActor; }),
|
SideEffect((_ctx,[_eventName,clientActor],self)=>{ self.context.clientActor=clientActor; }),
|
||||||
Goto('sending-response')
|
Goto('sending-response')
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
State('sending-response',
|
State('sending-response',
|
||||||
On('entry',
|
On('entry',
|
||||||
Do(log),
|
SideEffect(log),
|
||||||
Do(startTimer)
|
SideEffect(startTimer)
|
||||||
),
|
),
|
||||||
On('timer-finished',
|
On('timer-finished',
|
||||||
Do(sendResponse),
|
SideEffect(sendResponse),
|
||||||
Goto('awaiting-request')
|
Goto('awaiting-request')
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user