Skip to content

Instantly share code, notes, and snippets.

@lifeart
Created May 21, 2024 17:16
Show Gist options
  • Save lifeart/b6fc9ec2e111a12bb78a0558ef5afa11 to your computer and use it in GitHub Desktop.
Save lifeart/b6fc9ec2e111a12bb78a0558ef5afa11 to your computer and use it in GitHub Desktop.
Signals
// one of possible signals implementations
const USED_SIGNALS: Set<Signal> = new Set();
const RELATED_WATCHERS: WeakMap<Computed, Set<Watcher>> = new WeakMap();
const COMPUTED_SIGNALS: WeakMap<Signal, Set<Computed>> = new WeakMap();
class Signal {
value: any;
get() {
USED_SIGNALS.add(this);
return this.value;
}
set(value: any) {
this.value = value;
const Watchers: Set<Watcher> = new Set();
COMPUTED_SIGNALS.get(this)?.forEach((computed) => {
computed.isValid = false;
RELATED_WATCHERS.get(computed)!.forEach((watcher) => {
if (watcher.isWatching) {
watcher.pending.add(computed);
Watchers.add(watcher);
}
});
});
Watchers.forEach((watcher) => {
watcher.callback();
});
}
}
class Computed {
fn: Function;
relatedSignals: Set<Signal> = new Set();
isValid = false;
result: any;
constructor(fn = () => {}) {
this.fn = fn;
}
get() {
if (this.isValid) {
this.relatedSignals.forEach((signal) => {
USED_SIGNALS.add(signal);
});
return this.result;
}
const oldSignals = new Set(USED_SIGNALS);
USED_SIGNALS.clear();
try {
this.relatedSignals.forEach((signal) => {
COMPUTED_SIGNALS.get(signal)!.delete(this);
});
this.result = this.fn();
this.isValid = true;
return this.result;
} finally {
this.relatedSignals = new Set(USED_SIGNALS);
oldSignals.forEach((signal) => {
USED_SIGNALS.add(signal);
});
this.relatedSignals.forEach((signal) => {
if (!COMPUTED_SIGNALS.has(signal)) {
COMPUTED_SIGNALS.set(signal, new Set());
}
COMPUTED_SIGNALS.get(signal)!.add(this);
});
}
}
}
class Watcher {
constructor(callback: Function) {
this.callback = callback;
}
watched: Set<Computed> = new Set();
pending: Set<Computed> = new Set();
callback: Function;
isWatching = true;
watch(computed?: Computed) {
if (!computed) {
this.isWatching = true;
return;
}
if (!RELATED_WATCHERS.has(computed)) {
RELATED_WATCHERS.set(computed, new Set());
}
RELATED_WATCHERS.get(computed)!.add(this);
this.watched.add(computed);
}
unwatch(computed: Computed) {
this.watched.delete(computed);
RELATED_WATCHERS.get(computed)!.delete(this);
}
getPending() {
try {
return Array.from(this.pending);
} finally {
this.pending.clear();
this.isWatching = false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment