Timothy Farrell 863f61eea9 De-duplicate stream runs.
Don't eval multiple times when called multiple times in quick succession.
2018-06-13 11:07:04 -05:00

64 lines
1.5 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;
let currentProcess;
// Compute new value, call subscribers if changed.
const accessor = function _stream() {
if (!isDirty) {
return Promise.resolve(val);
}
if (!currentProcess) {
currentProcess = Promise.all(dependencies.map(call))
.then(params => fn.apply(null, params))
.then(res => {
const newId = hash(res);
isDirty = false;
if (oldId !== newId) {
oldId = newId;
val = res;
accessor.fire(val);
}
return val;
})
.finally(_ => {
isDirty = false;
currentProcess = null;
});
}
return currentProcess;
};
// 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;
}