From 0f713f9afee0d741391cd351c89569ab3ff2b30f Mon Sep 17 00:00:00 2001 From: REllEK-IO Date: Mon, 6 May 2024 10:12:25 -0700 Subject: [PATCH] v0.1.59 --- Axium.md | 4 +- README.md | 5 + src/model/axium.ts | 18 +-- src/model/stagePlanner.ts | 2 + src/test/debounceMethods.test.ts | 192 +++++++++++++++---------------- 5 files changed, 115 insertions(+), 106 deletions(-) diff --git a/Axium.md b/Axium.md index 46eb05f..dc036e2 100644 --- a/Axium.md +++ b/Axium.md @@ -137,5 +137,5 @@ Note these strategies can be broken into two parts responsibly, one to be ran vi *Note* That the addition of the axium concept itself is an addition departure from the FLUX architecture. This will be refined over time as specifics needs grow and should be seen as a work in progress. But, this should also be limited in its functionality to allow for the addition of concepts to expand the total functionality of the Axium paradigm. ## Axium Modes -* Permissive Mode - This Mode uses the simple trick of setTimeout(() => {}, 0) to allow for the axium to have some non blocking behavior. As this functionality directly engages with the event loop. In addition this mode will emit the internal concepts to the base and outer plans. -* Blocking Mode - This would be the synchronous mode behavior of the axium in order to allow for internal modifications of the axium's set of concepts. While blocking the potential of outside subscriptions to be notified of additional state changes of the application. And thus potential conflicts of dispatched actions during these changes. Even if dispatched and the concepts are removed, these actions will added to the badActions list. \ No newline at end of file +* Permissive Mode - This Mode enables the usage of asynchronous tasks via the setTimeout(() => {}, 0) trick. While maintaining non blocking behavior through the axium tail property that stores actions first in first out que. In addition this mode will emit the internal concepts to the base and outer plans. +* Blocking Mode - This mode forces synchronous mode behavior of the axium in order to allow for internal modifications of the axium's set of concepts. While blocking the potential of outside subscriptions to be notified of additional state changes of the application. And thus potential conflicts of dispatched actions during these changes. Even if dispatched and the concepts are removed, these actions will added to the badActions list. \ No newline at end of file diff --git a/README.md b/README.md index 3e61a7d..91288fb 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,11 @@ When in doubt simplify. * [Unified Turing Machine](https://github.com/Phuire-Research/Stratimux/blob/main/The-Unified-Turing-Machine.md) - The governing concept for this entire framework. ## Change Log ![Tests](https://github.com/Phuire-Research/Stratimux/actions/workflows/node.js.yml/badge.svg) +### Strong Fast **BREAKING** v0.1.59 5/06/24 +* Removed the setTimeout trick in favor of a new tail property added to the axium concept, this paves the way for this pattern to be completely responsible for its own implementation. + * **BREAKING** Method Subjects are now a tuple of [action: Action, async: Boolean]. This allows for the old setTimeout trick to be used in case the action stream isn't kicked into gear. + * This change is only breaking if you have implemented your own custom methods, please see src/model/method.ts for reference. +* Ensured that plans that conclude with an active beat, will have their timers removed. ### v0.1.58 5/03/24 * Ensured that changes that happen between a stage's beat interval are accumulated ### v0.1.57 5/02/24 diff --git a/src/model/axium.ts b/src/model/axium.ts index 18d1634..1bf95be 100644 --- a/src/model/axium.ts +++ b/src/model/axium.ts @@ -26,6 +26,7 @@ import { } from '../concepts/axium/qualities/appendActionListToDialog.quality'; import { axiumPreClose } from '../concepts/axium/qualities/preClose.quality'; import { StagePlanner, Staging } from './stagePlanner'; +import { axiumKick } from '../concepts/axium/qualities/kick.quality'; export const blockingMethodSubscription = (tail: Action[], action: Action) => { if ( @@ -63,14 +64,12 @@ export const defaultMethodSubscription = (tail: Action[], action$: Subject { + tail.push(appendToDialog); + tail.push(action); if (async) { setTimeout(() => { - tail.push(action); - action$.next(appendToDialog); + action$.next(axiumKick()); }, 0); - } else { - tail.push(appendToDialog); - tail.push(action); } // }, 0); } else if ( @@ -78,9 +77,12 @@ export const defaultMethodSubscription = (tail: Action[], action$: Subject { tail.push(action); - // }, 0); + if (async) { + setTimeout(() => { + action$.next(axiumKick()); + }, 0); + } } }; @@ -153,7 +155,7 @@ export function createAxium( } const modes = _concepts[0].mode as Mode[]; const mode = modes[modeIndex] as Mode; - console.log('STREAM', action, mode); + // console.log('STREAM', action, mode); mode([action, _concepts, _axiumState.action$, _axiumState.concepts$]); const nextAction = getAxiumState(concepts).tail.shift(); if (nextAction) { diff --git a/src/model/stagePlanner.ts b/src/model/stagePlanner.ts index 78ecc02..2ef9c53 100644 --- a/src/model/stagePlanner.ts +++ b/src/model/stagePlanner.ts @@ -392,6 +392,8 @@ export class UnifiedSubject extends Subject { protected deletePlan(planId: number) { const plan = this.currentPlans.get(planId); if (plan) { + plan.timer.forEach(timer => clearTimeout(timer)); + plan.timer = []; this.currentPlans.delete(planId); const selectors = plan.stages[plan.stage]?.selectors; if (selectors) { diff --git a/src/test/debounceMethods.test.ts b/src/test/debounceMethods.test.ts index 7420fd7..02db447 100644 --- a/src/test/debounceMethods.test.ts +++ b/src/test/debounceMethods.test.ts @@ -32,103 +32,103 @@ import { createStage } from '../model/stagePlanner'; jest.setTimeout(30000); -// test('Debounce method prevent excess count', (done) => { -// const experiment = createExperimentConcept(createExperimentState(), [experimentDebounceNextActionNodeQuality]); -// const axium = createAxium('Experiment async method creator with State', [createCounterConcept(), experiment]); -// const plan = axium.plan('Experiment debounce add one', [ -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((concepts, _) => { -// const counterState = selectState(concepts, counterName); -// console.log('Debounce HIT 4', counterState); -// if (counterState?.count === 1) { -// console.log('Final Debounce HIT 4', counterState); -// expect(counterState.count).toBe(1); -// plan.conclude(); -// done(); -// } -// }) -// ]); -// }); +test('Debounce method prevent excess count', (done) => { + const experiment = createExperimentConcept(createExperimentState(), [experimentDebounceNextActionNodeQuality]); + const axium = createAxium('Experiment async method creator with State', [createCounterConcept(), experiment]); + const plan = axium.plan('Experiment debounce add one', [ + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((concepts, _) => { + const counterState = selectState(concepts, counterName); + console.log('Debounce HIT 4', counterState); + if (counterState?.count === 1) { + console.log('Final Debounce HIT 4', counterState); + expect(counterState.count).toBe(1); + plan.conclude(); + done(); + } + }) + ]); +}); -// test('Async debounce method prevent excess count', (done) => { -// const experiment = createExperimentConcept(createExperimentState(), [experimentAsyncDebounceNextActionNodeQuality]); -// const axium = createAxium('Experiment async debounce', [createCounterConcept(), experiment]); -// const plan = axium.plan('Experiment async debounce add one', [ -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((concepts, _) => { -// const counterState = selectState(concepts, counterName); -// console.log('Async Debounce HIT 4', counterState); -// if (counterState?.count === 1) { -// console.log('FINAL Async Debounce HIT 4', counterState); -// expect(counterState.count).toBe(1); -// plan.conclude(); -// } -// }) -// ]); -// setTimeout(() => { -// const secondPlan = axium.plan('Second experiment async debounce add one', [ -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((_, dispatch) => { -// dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { -// iterateStage: true -// }); -// }), -// createStage((concepts, _) => { -// const counterState = selectState(concepts, counterName); -// console.log('Async 2 Debounce HIT 4', counterState); -// if (counterState?.count === 2) { -// console.log('FINAL Async 2 Debounce HIT 4', counterState); -// expect(counterState.count).toBe(2); -// secondPlan.conclude(); -// axium.close(); -// setTimeout(() => { -// done(); -// }, 500); -// } -// }) -// ]); -// // Axium must be primed, therefore we kick it back into gear. -// // Downside of halting quality. -// axium.dispatch(axiumKick()); -// }, 1000); -// }); +test('Async debounce method prevent excess count', (done) => { + const experiment = createExperimentConcept(createExperimentState(), [experimentAsyncDebounceNextActionNodeQuality]); + const axium = createAxium('Experiment async debounce', [createCounterConcept(), experiment]); + const plan = axium.plan('Experiment async debounce add one', [ + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((concepts, _) => { + const counterState = selectState(concepts, counterName); + console.log('Async Debounce HIT 4', counterState); + if (counterState?.count === 1) { + console.log('FINAL Async Debounce HIT 4', counterState); + expect(counterState.count).toBe(1); + plan.conclude(); + } + }) + ]); + setTimeout(() => { + const secondPlan = axium.plan('Second experiment async debounce add one', [ + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((_, dispatch) => { + dispatch(strategyBegin(experimentAsyncDebounceAddOneStrategy()), { + iterateStage: true + }); + }), + createStage((concepts, _) => { + const counterState = selectState(concepts, counterName); + console.log('Async 2 Debounce HIT 4', counterState); + if (counterState?.count === 2) { + console.log('FINAL Async 2 Debounce HIT 4', counterState); + expect(counterState.count).toBe(2); + secondPlan.conclude(); + axium.close(); + setTimeout(() => { + done(); + }, 500); + } + }) + ]); + // Axium must be primed, therefore we kick it back into gear. + // Downside of halting quality. + axium.dispatch(axiumKick()); + }, 1000); +}); test('Debounce Method Test with State id comparison', (done) => { const experiment = createExperimentConcept(createExperimentState(), [experimentDebounceIterateIdThenReceiveInMethodQuality]);