Skip to content

Instantly share code, notes, and snippets.

@mizchi
Created April 14, 2021 04:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mizchi/050b2442ba04df3f462d1c0230a664dd to your computer and use it in GitHub Desktop.
Save mizchi/050b2442ba04df3f462d1c0230a664dd to your computer and use it in GitHub Desktop.
type EventBase = {
__uid?: string;
__ts?: number;
};
type PubSub<Event> = {
dispatch: (ev: Event) => void;
subscribe: (fn: (ev: Event) => void) => void;
};
type Store<Event extends EventBase> = PubSub<Event> & {
createContext(id: string): ContextStore<Event>;
history: Event[];
};
type ContextStore<Event extends EventBase> = PubSub<Event> & {
start(): void;
};
function createStore<Event extends EventBase>(): Store<Event> {
const history: Event[] = [];
const handlers: Set<(ev: Event) => void> = new Set();
const dispatch = (ev: Event) => {
ev.__ts = Date.now();
if (ev.__uid == null) {
history.push(ev);
}
handlers.forEach((fn) => {
fn(ev);
});
};
const subscribe = (handler: (ev: Event) => void) => {
handlers.add(handler);
};
function createContext(uid: string) {
let ready = false;
let localQueue: Array<Event> = [];
return {
dispatch: (ev: Event) => {
if (!ready) {
localQueue.push(ev);
} else {
dispatch(ev);
}
},
subscribe: (handler: (ev: Event) => void) => {
subscribe((ev) => {
if (ev.__uid && uid !== ev.__uid) {
return;
}
handler(ev);
});
},
start() {
ready = true;
// local dispatch
history.forEach((ev) => {
dispatch({ ...ev, __uid: uid });
});
// distribute local events for all
localQueue.forEach((item) => dispatch(item));
},
};
}
return {
dispatch,
subscribe,
createContext,
history,
};
}
type MyEvent = EventBase & {
type: string;
};
it("xxx", async () => {
let p1c = 0;
let p2c = 0;
let p3c_lazy = 0;
const store = createStore<MyEvent>();
function p1(store: ContextStore<MyEvent>) {
store.dispatch({ type: "xxx" });
store.subscribe((ev) => {
console.log("[p1] received", ev);
if (ev.type === "yyy") {
p1c++;
}
});
}
function p2(store: ContextStore<MyEvent>) {
store.dispatch({ type: "yyy" });
store.subscribe((ev) => {
console.log("[p2] received", ev);
if (ev.type === "yyy") {
p2c++;
}
});
}
async function p3(store: ContextStore<MyEvent>) {
await new Promise((r) => setTimeout(r, 100));
store.dispatch({ type: "zzz" });
return store.subscribe((ev) => {
if (ev.type === "yyy") {
p3c_lazy++;
}
});
}
await Promise.all(
[p1, p2, p3].map(async (fn) => {
const uid = Math.random().toString(32).substr(2);
const ctx = store.createContext(uid);
await fn(ctx);
ctx.start();
})
);
expect(p1c).toBe(1);
expect(p2c).toBe(1);
expect(p3c_lazy).toBe(1);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment