kr-observable
Version:
Adds reactivity power for your JavaScript
83 lines (82 loc) • 2.31 kB
JavaScript
import { Global } from './global.js';
const queue = Global.queue;
const notifier = Global.notifier;
export class Admin {
static meta = { key: '', adm: new Admin('') };
owner;
deps = new Map;
listeners;
changes = new Set;
current = null;
queued = false;
constructor(owner) {
this.owner = owner;
}
subscribe(property, runnable) {
let list = this.deps.get(property);
if (!list) {
list = new Set;
this.deps.set(property, list);
}
if (!list.has(runnable)) {
list.add(runnable);
if (!runnable.deps?.has(list)) {
runnable.deps?.add(list);
}
}
}
unsubscribe(property, runnable) {
this.deps.get(property)?.delete(runnable);
}
report(property, value) {
this.listeners?.forEach(cb => cb(property, value));
if (!this.deps.has(property))
return;
this.changes.add(property);
if (Global.action) {
queue.add(this);
}
else {
if (this.queued)
return;
this.queued = true;
queueMicrotask(this.enqueueBatch);
}
}
enqueueBatch = () => {
this.batch();
this.queued = false;
};
batch(flag = false) {
if (this.changes.size === 0)
return;
const changes = this.changes;
if (changes.size > 1) {
const copy = new Set();
for (const key of this.deps.keys()) {
if (changes.has(key))
copy.add(key);
}
this.changes = copy;
}
for (const change of this.changes) {
this.changes.delete(change);
this.current = this.deps.get(change);
for (const sub of this.current) {
if (sub.runId !== notifier.runId) {
if (sub.active) {
return;
}
if (flag && sub.computed) {
return this.changes.add(change);
}
notifier.notify(sub, changes);
}
}
this.current = null;
}
}
static batch(adm) {
adm.batch();
}
}