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);