UNPKG

vega-dataflow

Version:

Reactive dataflow processing.

92 lines (82 loc) 3.52 kB
import Operator from '../Operator.js'; import {isChangeSet} from '../ChangeSet.js'; import {constant, extend, isFunction} from 'vega-util'; const SKIP = {skip: true}; /** * Perform operator updates in response to events. Applies an * update function to compute a new operator value. If the update function * returns a {@link ChangeSet}, the operator will be pulsed with those tuple * changes. Otherwise, the operator value will be updated to the return value. * @param {EventStream|Operator} source - The event source to react to. * This argument can be either an EventStream or an Operator. * @param {Operator|function(object):Operator} target - The operator to update. * This argument can either be an Operator instance or (if the source * argument is an EventStream), a function that accepts an event object as * input and returns an Operator to target. * @param {function(Parameters,Event): *} [update] - Optional update function * to compute the new operator value, or a literal value to set. Update * functions expect to receive a parameter object and event as arguments. * This function can either return a new operator value or (if the source * argument is an EventStream) a {@link ChangeSet} instance to pulse * the target operator with tuple changes. * @param {object} [params] - The update function parameters. * @param {object} [options] - Additional options hash. If not overridden, * updated operators will be skipped by default. * @param {boolean} [options.skip] - If true, the operator will * be skipped: it will not be evaluated, but its dependents will be. * @param {boolean} [options.force] - If true, the operator will * be re-evaluated even if its value has not changed. * @return {Dataflow} */ export default function(source, target, update, params, options) { const fn = source instanceof Operator ? onOperator : onStream; fn(this, source, target, update, params, options); return this; } function onStream(df, stream, target, update, params, options) { const opt = extend({}, options, SKIP); let func, op; if (!isFunction(target)) target = constant(target); if (update === undefined) { func = e => df.touch(target(e)); } else if (isFunction(update)) { op = new Operator(null, update, params, false); func = e => { op.evaluate(e); const t = target(e), v = op.value; isChangeSet(v) ? df.pulse(t, v, options) : df.update(t, v, opt); }; } else { func = e => df.update(target(e), update, opt); } stream.apply(func); } function onOperator(df, source, target, update, params, options) { if (update === undefined) { source.targets().add(target); } else { const opt = options || {}, op = new Operator(null, updater(target, update), params, false); op.modified(opt.force); op.rank = source.rank; // immediately follow source source.targets().add(op); // add dependency if (target) { op.skip(true); // skip first invocation op.value = target.value; // initialize value op.targets().add(target); // chain dependencies df.connect(target, [op]); // rerank as needed, #1672 } } } function updater(target, update) { update = isFunction(update) ? update : constant(update); return target ? function(_, pulse) { const value = update(_, pulse); if (!target.skip()) { target.skip(value !== this.value).value = value; } return value; } : update; }