Skip to content

Instantly share code, notes, and snippets.

@muromec
Created February 13, 2023 09:32
Show Gist options
  • Save muromec/d80219ef3ef6e307cf1d05bbfcfb5b0c to your computer and use it in GitHub Desktop.
Save muromec/d80219ef3ef6e307cf1d05bbfcfb5b0c to your computer and use it in GitHub Desktop.
import { computed, readonly, reactive, ref, watch } from 'vue'
function spawn(fn, pname, toParent = ()=> null) {
let current = null;
let state = reactive({});
let buffer = [];
let resolveExit;
let exitPromise = new Promise(resolve => {
resolveExit = resolve;
});
function send ({ to = pparent, ...msg }) {
let ret = to.next({ state, reply: toSelf, msg });
tick();
if (ret.done) {
resolveExit();
}
}
function tick () {
let msg;
while(msg = buffer.pop()) {
toParent({pname, ...msg});
}
}
function toSelf(msg) {
send({ to: current, ...msg});
}
function fromChild(msg) {
toSelf(msg);
}
function toBuffer(msg) {
buffer.push(msg);
}
function fork (fn, pname) {
return spawn(fn, pname, fromChild);
}
function wait () {
return exitPromise;
}
return (...args) => {
const process = {
pname,
send: toSelf,
fork,
toParent: toBuffer,
id: Symbol(pname),
state: readonly(state),
wait,
};
const task = watchExit(fn)(process, ...args);
current = task;
task.next();
toSelf({ type: 'INIT' });
return process;
}
}
function attach(supervisor, fn, pname) {
return (...args) => {
supervisor.send({ type: 'RUN', fn, args, pname});
}
}
function watchExit(fn) {
return function* (process, ...args) {
yield* fn(process, ...args);
process.toParent({ type: 'EXIT', pid: process.id});
}
}
function* runDispatch(name, fn) {
let state = null;
let msg;
while(true) {
({state, msg} = yield state);
//console.log('msg', name, ' <- ', msg);
let ret = fn(state, msg);
if (ret === 'STOPPED') {
break;
}
}
}
function* supervise({pname, toParent, fork}) {
yield* runDispatch(pname, (state, msg)=> {
if (msg.type === 'INIT') {
state.processes = [];
};
if (msg.type === 'RUN') {
const newProcess = fork(msg.fn, msg.pname)(...msg.args);
state.processes.push(newProcess);
}
if (msg.type === 'ERROR' || msg.type === 'ABORT') {
state.processes.forEach(p=> p.send({ type: 'ABORT'}));
}
if (msg.type === 'EXIT') {
state.processes = state.processes.filter(
iter=> iter.id !== msg.pid
);
if (state.processes.length === 0) {
return 'STOPPED';
}
}
if (msg.type === 'OK') {
toParent(msg);
}
});
}
function* xfetch({ pname, toParent, send: toSelf }, { url }) {
const controller = new AbortController();
const signal = controller.signal;
(async function do_request() {
try {
const res = await fetch(url.href, { signal });
const text = await res.text();
toSelf({ type: 'OK', text });
} catch (e) {
const isAborted = (e instanceof DOMException && e.name === 'AbortError');
if (isAborted) {
toSelf({ type: 'ABORTED', pname });
} else {
//console.log('e', e);
toSelf({ type: 'ERROR', pname });
}
}
toSelf({ type: 'DONE' });
})();
yield* runDispatch(pname, (state, msg)=> {
if (msg.type === 'INIT') {
state.code = 'pending';
}
if (msg.type === 'ABORT') {
controller.abort();
}
if (msg.type === 'DONE') {
return 'STOPPED';
}
if (msg.type === 'ABORTED') {
toParent(msg);
state.code = 'aborted';
}
if (msg.type === 'ERROR') {
toParent(msg);
state.code = 'failed';
}
if (msg.type === 'OK') {
toParent(msg);
state.code = 'ok';
}
});
}
function* main({ pname, fork }) {
const s = fork(supervise, 'super')();
const urls = [
new URL('https://api.myip.com'),
new URL('https://x.myip.com'),
];
for(let url of urls) {
attach(s, xfetch, `xfetch ${url.href}`)({ url });
}
yield* runDispatch(pname, (state, msg)=> {
if (msg.type === 'INIT') {
state.s = s;
}
if (msg.type === 'ABORT') {
s.send({ type: 'ABORT'});
}
if (msg.type === 'EXIT') {
state.s = null;
}
if (msg.type === 'OK') {
console.log('data', msg.text);
return 'STOPPED';
}
});
}
const m = spawn(main, 'main')();
const states = computed(() => {
if (!m.state.s) {
return null;
}
return m.state.s.state.processes.map(p => p.state);
});
watch(states, (value)=> {
console.log('s', value);
}, { immediate: true });
//m.send({ type: 'ABORT', p: null});
await m.wait();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment