Skip to content

Instantly share code, notes, and snippets.

@tzkmx
Last active October 16, 2024 23:06
Show Gist options
  • Save tzkmx/8d9dfaa552b2469deca4dbeb0777bf20 to your computer and use it in GitHub Desktop.
Save tzkmx/8d9dfaa552b2469deca4dbeb0777bf20 to your computer and use it in GitHub Desktop.
Simple Machine recording requests and simulating responses sequentially
const { assign, createMachine, interpret } = require('xstate');
const { inspect } = require('util');
const queueMachine = createMachine({
id: 'queueMachine',
initial: 'idle',
context: {
invokes: [],
processing: {},
results: []
},
on: {
go: {
actions: [assign((ctx, ev) => ({
invokes: [...ctx.invokes, { id: ev.id, ts: (new Date()).toISOString() }]
}))],
target: 'loadInvoke'
},
setContext: {
actions: assign((_ctx, ev) => ({
..._ctx,
...ev
}))
}
},
states: {
idle: {
always: [
{
target: 'loadInvoke',
cond: (ctx) => Boolean(ctx.invokes.length)
},
{
target: 'waiting',
cond: (ctx) => Boolean(Object.keys(ctx.processing).length)
}
]
},
loadInvoke: {
entry: assign((ctx) => {
const first = ctx.invokes.shift();
return {
invokes: ctx.invokes,
processing: {
...ctx.processing,
[first.id]: first
}
}
}),
after: {
100: { target: 'waiting' }
}
},
waiting: {
invoke: {
src: 'simulate',
onDone: {
actions: [
assign({
processing: (ctx, ev) => {
const processing = {...ctx.processing};
const id = ev.data?.id
if (id && processing[id]) {
delete processing[id];
}
console.log({ done: ev.data,
np: processing
});
return processing;
},
results: (ctx, ev) => [...ctx.results, ev.data]
}),
],
target: 'idle'
},
onFailure: {
target: 'fail'
}
}
},
fail: {
type: 'final'
}
}
}, {
services: {
simulate: (ctx, _ev) => {
return new Promise((res, rej) => {
const processing = ctx.processing;
const waiting = Object.keys(processing);
const req = processing[waiting[0]];
console.log(inspect({ req, invokes: ctx.invokes, processing }, false, 10));
setTimeout(() => {
return res({ id: req.id, start: req.ts, end: (new Date()).toISOString() });
}, 5_000 * Math.random());
})
}
}
});
let actor;
const getActor = () => {
if (actor) {
actor.stop();
}
actor = interpret(queueMachine);
actor.onTransition((s) => {
if (s.changed) {
printer(s);
}
});
return actor
}
function send(...args) {
(actor || getActor()).send(...args);
}
exports.queueMachine = queueMachine;
exports.getActor = getActor;
exports.send = send;
/**
* @typedef Actor
* @property {*} context
* @property {string|object} value "state"
* @property {{ _event: { data: {*} }, historyValue: {current: string, states: {object}} } history
* @property {() => [*]} toStrings()
*/
function printer (m) {
console.log(inspect({
ctx: m.context,
state: m.value,
str: m.toStrings()
}, false, 10));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment