57 lines
1.5 KiB
JavaScript
57 lines
1.5 KiB
JavaScript
import { id, registerFire, registerSubscriptions, call } from './util.js';
|
|
|
|
export const hashableComputed = hash => (fn, dependencies = []) => {
|
|
let subscribers = [];
|
|
let isDirty = true;
|
|
let val;
|
|
let oldId;
|
|
const params = dependencies.map(d => (d._lock ? d._lock : d));
|
|
const unlockableDeps = dependencies.map(d => d._unlock).filter(id);
|
|
|
|
// Compute new value, call subscribers if changed.
|
|
const accessor = function _computed() {
|
|
if (isDirty) {
|
|
const newVal = fn.apply(null, params.map(call));
|
|
isDirty = false;
|
|
const newId = hash(newVal);
|
|
if (oldId !== newId) {
|
|
oldId = newId;
|
|
val = newVal;
|
|
accessor._fire(val);
|
|
}
|
|
unlockableDeps.forEach(call);
|
|
}
|
|
return val;
|
|
};
|
|
|
|
// Add child nodes to the logic graph (value-based)
|
|
accessor.subscribe = registerSubscriptions(subscribers);
|
|
accessor._fire = registerFire(subscribers);
|
|
|
|
// Receive dirty flag from parent logic node (dependency). Pass it down.
|
|
accessor._setDirty = function setDirty() {
|
|
if (!isDirty) {
|
|
isDirty = true;
|
|
subscribers.forEach(s => s._setDirty && s._setDirty());
|
|
}
|
|
return subscribers.length && accessor;
|
|
};
|
|
|
|
// Remove this node from the logic graph completely
|
|
accessor.detach = () => {
|
|
subscribers = [];
|
|
dependentSubscriptions.forEach(call);
|
|
};
|
|
|
|
// Remove child nodes from the logic graph
|
|
accessor.unsubscribeAll = () => {
|
|
subscribers = [];
|
|
};
|
|
|
|
const dependentSubscriptions = dependencies.map(d => d.subscribe(accessor._setDirty));
|
|
|
|
return accessor;
|
|
};
|
|
|
|
export const computed = hashableComputed(id);
|