52 lines
1.3 KiB
JavaScript
52 lines
1.3 KiB
JavaScript
import { id, registerFire, registerSubscriptions, call } from './util.js';
|
|
|
|
export function stream(fn, dependencies = [], hash = id) {
|
|
let subscribers = [];
|
|
let isDirty = true;
|
|
let val;
|
|
let oldId;
|
|
|
|
// Compute new value, call subscribers if changed.
|
|
const accessor = async function _computed() {
|
|
if (isDirty) {
|
|
const newVal = await fn.apply(null, await Promise.all(dependencies.map(call)));
|
|
isDirty = false;
|
|
const newId = hash(newVal);
|
|
if (oldId !== newId) {
|
|
oldId = newId;
|
|
val = newVal;
|
|
accessor.fire(val);
|
|
}
|
|
}
|
|
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;
|
|
}
|