Skip to content

Instantly share code, notes, and snippets.

@rasmusmerzin
Last active May 29, 2022 06:48
Show Gist options
  • Save rasmusmerzin/0668e60e3bf9b0118e2f212beea4d15a to your computer and use it in GitHub Desktop.
Save rasmusmerzin/0668e60e3bf9b0118e2f212beea4d15a to your computer and use it in GitHub Desktop.
Typescript LockMap<T>
import { LockMap, LockMapEvent } from "./lock-map";
const { Release, Change, Acquire } = LockMapEvent;
describe("LockMap<void>", () => {
let events: LockMapEvent[];
let lockSet: LockMap<void>;
beforeEach(() => {
events = [];
lockSet = new LockMap();
for (const type of <LockMapEvent[]>Object.values(LockMapEvent))
lockSet.addEventListener(type, () => events.push(type));
});
it("set", () => {
let ctrl: AbortController;
const ctrls = Array.from(Array(2), () => new AbortController());
ctrls.forEach((ctrl) => lockSet.set(ctrl));
expect(events).toStrictEqual([Acquire, Change]);
events = [];
ctrls.pop()!.abort();
expect(events).toStrictEqual([Change]);
events = [];
lockSet.set((ctrl = new AbortController()));
ctrls.push(ctrl);
expect(events).toStrictEqual([Change]);
events = [];
ctrls.splice(0, Infinity).forEach((ctrl) => ctrl.abort());
expect(events).toStrictEqual([Change, Release]);
events = [];
lockSet.set((ctrl = new AbortController()));
expect(events).toStrictEqual([Acquire]);
events = [];
ctrl.abort();
expect(events).toStrictEqual([Release]);
});
});
export enum LockMapEvent {
Acquire = "acquire",
Change = "change",
Release = "release",
}
export class LockMap<T> extends EventTarget {
readonly map = new Map<AbortController, T>();
private delete(ctrl: AbortController) {
if (!this.map.size) return;
this.map.delete(ctrl);
if (this.map.size) this.dispatchEvent(new Event(LockMapEvent.Change));
else this.dispatchEvent(new Event(LockMapEvent.Release));
}
private has = (ctrl: AbortController, value: T) =>
this.map.has(ctrl) && this.map.get(ctrl) === value;
set = (ctrl: AbortController, value: T) => {
if (ctrl.signal.aborted || this.has(ctrl, value)) return;
const previousSize = this.map.size;
this.map.set(ctrl, value);
ctrl.signal.addEventListener("abort", () => this.delete(ctrl));
if (previousSize) return this.dispatchEvent(new Event(LockMapEvent.Change));
else return this.dispatchEvent(new Event(LockMapEvent.Acquire));
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment