diff --git a/dash-renderer/src/actions/dependencies_ts.ts b/dash-renderer/src/actions/dependencies_ts.ts index 8b4a71c08e..1ad21f9da5 100644 --- a/dash-renderer/src/actions/dependencies_ts.ts +++ b/dash-renderer/src/actions/dependencies_ts.ts @@ -207,16 +207,57 @@ export function getPriority( return map(i => Math.min(i, 35).toString(36), priority).join(''); } +export function getAllSubsequentOutputsForCallback( + graphs: any, + paths: any, + callback: ICallback +) { + let callbacks: ICallback[] = [callback]; + let touchedOutputs: {[key: string]: boolean} = {}; + + while (callbacks.length) { + const outputs = filter( + o => !touchedOutputs[combineIdAndProp(o)], + flatten(map(cb => flatten(cb.getOutputs(paths)), callbacks)) + ); + + touchedOutputs = reduce( + (touched, o) => assoc(combineIdAndProp(o), true, touched), + touchedOutputs, + outputs + ); + + callbacks = flatten( + map( + ({id, property}: any) => + getCallbacksByInput( + graphs, + paths, + id, + property, + INDIRECT, + false + ), + outputs + ) + ); + } + + return touchedOutputs; +} + export const getReadyCallbacks = ( paths: any, candidates: ICallback[], - callbacks: ICallback[] = candidates + callbacks: ICallback[] = candidates, + graphs:any = {} ): ICallback[] => { // Skip if there's no candidates if (!candidates.length) { return []; } + console.log("dependencies:getReadyCallbacks callbacks", callbacks, 'candidates', candidates); // Find all outputs of all active callbacks const outputs = map( combineIdAndProp, @@ -227,21 +268,62 @@ export const getReadyCallbacks = ( ) ); + console.log("dependencies:getReadyCallbacks outputs", outputs) + // Make `outputs` hash table for faster access - const outputsMap: {[key: string]: boolean} = {}; + var outputsMap: {[key: string]: boolean} = {}; + forEach(output => (outputsMap[output] = true), outputs); - // Find `requested` callbacks that do not depend on a outstanding output (as either input or state) + + // find all the outputs touched by activeCallbacks + // remove this check if graph is accessible all the time + if(graphs) { + //not sure if graph will be accessible all the time + const allTouchedOutputs:{[key:string]: boolean}[] = flatten(map( + (cb)=> getAllSubsequentOutputsForCallback( + graphs, + paths, + cb + ), + callbacks)) + + console.log("dependencies:getReadyCallbacks allTouchedOutputs", allTouchedOutputs) + + outputsMap = Object.assign( ...allTouchedOutputs ); + } + + // Find `requested` callbacks that do not depend on an outstanding output (as either input or state) // Outputs which overlap an input do not count as an outstanding output + + let existsInCurrentCallbackPath = (cb:any)=>{ + return outputsMap[combineIdAndProp(cb)] + } + + let outStandingCb = (cb:any) =>{ + console.log('dependencies:getReadyCallbacks cb', cb); + + let inputs = flatten(cb.getInputs(paths)); + let outputs = flatten(cb.getOutputs(paths)); + + console.log('dependencies:getReadyCallbacks inputs', inputs); + console.log('dependencies:getReadyCallbacks outputs', outputs); + + let IODifference = difference(inputs, outputs) + + console.log('dependencies:getReadyCallbacks IODifference', IODifference); + + let noOutstandingOutputExists = all( + cbp => !existsInCurrentCallbackPath(cbp), + IODifference + ) + + console.log('dependencies:getReadyCallbacks outputsMap', outputsMap, 'outStandingOutputExists', noOutstandingOutputExists) + return noOutstandingOutputExists; + } + return filter( - cb => - all( - cbp => !outputsMap[combineIdAndProp(cbp)], - difference( - flatten(cb.getInputs(paths)), - flatten(cb.getOutputs(paths)) - ) - ), + cb => outStandingCb(cb), candidates ); }; @@ -344,15 +426,15 @@ export function includeObservers( var validCbs = [... flattenedCbs]; - //TODO: not sure if this is optimal, maybe some information already exists in graphs DS - for(let i=0; i = { observer: async ({dispatch, getState}) => { @@ -62,7 +63,8 @@ const observer: IStoreObserverDefinition = { const { callbacks, callbacks: {prioritized, blocked, executing, watched, stored}, - paths + paths, + graphs } = getState(); let { callbacks: {requested} @@ -73,6 +75,7 @@ const observer: IStoreObserverDefinition = { const initialRequested = requested.slice(0); + // TODO: this should be a property rather than a property of a state, rather than a function const pendingCallbacks = getPendingCallbacks(callbacks); console.log("observer:requestedCallbacks: pending", pendingCallbacks); @@ -231,16 +234,19 @@ const observer: IStoreObserverDefinition = { it will be updated for real */ requested = concat(difference(requested, rRemoved), rAdded); - + console.log("observer:requestedCallbacks: requested after pruning", requested); /* 4. Find `requested` callbacks that do not depend on a outstanding output (as either input or state) */ let readyCallbacks = getReadyCallbacks( paths, requested, - pendingCallbacks + pendingCallbacks, + graphs ); + console.log("observer:requestedCallbacks: readyCallbacks", readyCallbacks); + let oldBlocked: ICallback[] = []; let newBlocked: ICallback[] = [];