-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
538 additions
and
117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/concepts/experiment/qualities/debounceAsyncNextActionNode.quality.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { MethodCreator, defaultReducer } from '../../../model/concept'; | ||
import { prepareActionCreator } from '../../../model/action'; | ||
import { createQuality } from '../../../model/concept'; | ||
import { createAsyncMethodDebounce } from '../../../model/method'; | ||
import { strategySuccess } from '../../../model/actionStrategy'; | ||
|
||
export const experimentAsyncDebounceNextActionNodeType = 'Experiment will debounce incoming actions within set duration asynchronously'; | ||
export const experimentAsyncDebounceNextActionNode = prepareActionCreator(experimentAsyncDebounceNextActionNodeType); | ||
|
||
export const experimentDebounceNextActionNodeMethodCreator: MethodCreator = () => createAsyncMethodDebounce((controller, action) => { | ||
setTimeout(() => { | ||
if (action.strategy) { | ||
controller.fire(strategySuccess(action.strategy)); | ||
} else { | ||
controller.fire(action); | ||
} | ||
}, 50); | ||
}, 500); | ||
|
||
export const asyncDebounceNextActionNodeQuality = createQuality( | ||
experimentAsyncDebounceNextActionNodeType, | ||
defaultReducer, | ||
experimentDebounceNextActionNodeMethodCreator | ||
); |
22 changes: 22 additions & 0 deletions
22
src/concepts/experiment/qualities/debounceNextActionNode.quality.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { MethodCreator, defaultReducer } from '../../../model/concept'; | ||
import { prepareActionCreator } from '../../../model/action'; | ||
import { createQuality } from '../../../model/concept'; | ||
import { createMethodDebounce } from '../../../model/method'; | ||
import { strategySuccess } from '../../../model/actionStrategy'; | ||
|
||
export const experimentDebounceNextActionNodeType = 'Experiment will debounce incoming actions within set duration'; | ||
export const experimentDebounceNextActionNode = prepareActionCreator(experimentDebounceNextActionNodeType); | ||
|
||
export const experimentDebounceNextActionNodeMethodCreator: MethodCreator = () => createMethodDebounce((action) => { | ||
if (action.strategy) { | ||
return strategySuccess(action.strategy); | ||
} else { | ||
return action; | ||
} | ||
}, 500); | ||
|
||
export const debounceNextActionNodeQuality = createQuality( | ||
experimentDebounceNextActionNodeType, | ||
defaultReducer, | ||
experimentDebounceNextActionNodeMethodCreator | ||
); |
33 changes: 33 additions & 0 deletions
33
src/concepts/experiment/strategies/asyncDebounceAddOne.strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { createStrategy, ActionStrategy, ActionStrategyParameters, createActionNode } from '../../../model/actionStrategy'; | ||
import { counterAdd } from '../../counter/qualities/add.quality'; | ||
import { experimentAsyncDebounceNextActionNode } from '../qualities/debounceAsyncNextActionNode.quality'; | ||
import { experimentDebounceNextActionNode } from '../qualities/debounceNextActionNode.quality'; | ||
|
||
export const experimentAsyncDebounceAddOneTopic = 'Async debounce add one'; | ||
export function experimentAsyncDebounceAddOneStrategy(): ActionStrategy { | ||
const stepTwo = createActionNode(counterAdd(), { | ||
successNode: null, | ||
successNotes: { | ||
preposition: '', | ||
denoter: 'One;', | ||
}, | ||
failureNode: null, | ||
agreement: 1000, | ||
}); | ||
const stepOne = createActionNode(experimentAsyncDebounceNextActionNode(), { | ||
successNode: stepTwo, | ||
successNotes: { | ||
preposition: '', | ||
denoter: 'One;', | ||
}, | ||
failureNode: null, | ||
agreement: 1000, | ||
}); | ||
|
||
const params: ActionStrategyParameters = { | ||
topic: experimentAsyncDebounceAddOneTopic, | ||
initialNode: stepOne, | ||
}; | ||
|
||
return createStrategy(params); | ||
} |
32 changes: 32 additions & 0 deletions
32
src/concepts/experiment/strategies/debounceAddOne.strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import { createStrategy, ActionStrategy, ActionStrategyParameters, createActionNode } from '../../../model/actionStrategy'; | ||
import { counterAdd } from '../../counter/qualities/add.quality'; | ||
import { experimentDebounceNextActionNode } from '../qualities/debounceNextActionNode.quality'; | ||
|
||
export const experimentDebounceAddOneTopic = 'Debounce add one'; | ||
export function experimentDebounceAddOneStrategy(): ActionStrategy { | ||
const stepTwo = createActionNode(counterAdd(), { | ||
successNode: null, | ||
successNotes: { | ||
preposition: '', | ||
denoter: 'One;', | ||
}, | ||
failureNode: null, | ||
agreement: 1000, | ||
}); | ||
const stepOne = createActionNode(experimentDebounceNextActionNode(), { | ||
successNode: stepTwo, | ||
successNotes: { | ||
preposition: '', | ||
denoter: 'One;', | ||
}, | ||
failureNode: null, | ||
agreement: 1000, | ||
}); | ||
|
||
const params: ActionStrategyParameters = { | ||
topic: experimentDebounceAddOneTopic, | ||
initialNode: stepOne, | ||
}; | ||
|
||
return createStrategy(params); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { | ||
MonoTypeOperatorFunction, | ||
Observable, | ||
OperatorFunction, | ||
SchedulerAction, | ||
SchedulerLike, | ||
Subscriber, | ||
Subscription, | ||
asyncScheduler | ||
} from 'rxjs'; | ||
import { Action } from './action'; | ||
import { axiumConclude } from '../concepts/axium/qualities/conclude.quality'; | ||
|
||
function hasLift(source: any): source is { lift: InstanceType<typeof Observable>['lift'] } { | ||
return typeof source?.lift === 'function'; | ||
} | ||
|
||
function operate<T, R>( | ||
init: (liftedSource: Observable<T>, subscriber: Subscriber<R>) => (() => void) | void | ||
): OperatorFunction<T, R> { | ||
return (source: Observable<T>) => { | ||
if (hasLift(source)) { | ||
// eslint-disable-next-line consistent-return | ||
return source.lift(function (this: Subscriber<R>, liftedSource: Observable<T>) { | ||
try { | ||
return init(liftedSource, this); | ||
} catch (err) { | ||
this.error(err); | ||
} | ||
}); | ||
} | ||
throw new TypeError('Unable to lift unknown Observable type'); | ||
}; | ||
} | ||
|
||
class OperatorSubscriber<T> extends Subscriber<T> { | ||
constructor( | ||
destination: Subscriber<any>, | ||
onNext?: (value: T) => void, | ||
onComplete?: () => void, | ||
onError?: (err: any) => void, | ||
private onFinalize?: () => void, | ||
private shouldUnsubscribe?: () => boolean | ||
) { | ||
super(destination); | ||
this._next = onNext | ||
? function (this: OperatorSubscriber<T>, value: T) { | ||
try { | ||
onNext(value); | ||
} catch (err) { | ||
destination.error(err); | ||
} | ||
} | ||
: super._next; | ||
this._error = onError | ||
? function (this: OperatorSubscriber<T>, err: any) { | ||
try { | ||
onError(err); | ||
} catch (error) { | ||
// Send any errors that occur down stream. | ||
destination.error(error); | ||
} finally { | ||
// Ensure finalization. | ||
this.unsubscribe(); | ||
} | ||
} | ||
: super._error; | ||
this._complete = onComplete | ||
? function (this: OperatorSubscriber<T>) { | ||
try { | ||
onComplete(); | ||
} catch (err) { | ||
// Send any errors that occur down stream. | ||
destination.error(err); | ||
} finally { | ||
// Ensure finalization. | ||
this.unsubscribe(); | ||
} | ||
} | ||
: super._complete; | ||
} | ||
|
||
unsubscribe() { | ||
if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) { | ||
const { closed } = this; | ||
super.unsubscribe(); | ||
!closed && this.onFinalize?.(); | ||
} | ||
} | ||
} | ||
|
||
function createOperatorSubscriber<T>( | ||
destination: Subscriber<any>, | ||
onNext?: (value: T) => void, | ||
onComplete?: () => void, | ||
onError?: (err: any) => void, | ||
onFinalize?: () => void | ||
): Subscriber<T> { | ||
return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize); | ||
} | ||
|
||
/** | ||
* This will prevent all actions for the specified duration, but will still emit actions as axiumConclude | ||
* Thus this needs to be taken into account in the Method using debounceAction if implemented directly. | ||
* But will be handled automatically in actionControllers and associated debounce createMethods. | ||
*/ | ||
export function debounceAction(dueTime: number, scheduler: SchedulerLike = asyncScheduler): MonoTypeOperatorFunction<Action> { | ||
return operate((source, subscriber) => { | ||
let activeTask: Subscription | null = null; | ||
let lastValue: Action | null = null; | ||
let lastTime: number | null = null; | ||
|
||
const emit = () => { | ||
if (activeTask) { | ||
activeTask.unsubscribe(); | ||
activeTask = null; | ||
const value = lastValue!; | ||
lastValue = null; | ||
subscriber.next(value); | ||
} | ||
}; | ||
function emitWhenIdle(this: SchedulerAction<unknown>) { | ||
const targetTime = lastTime! + dueTime; | ||
const now = scheduler.now(); | ||
if (now < targetTime) { | ||
activeTask = this.schedule(undefined, targetTime - now); | ||
subscriber.add(activeTask); | ||
return; | ||
} | ||
|
||
emit(); | ||
} | ||
|
||
source.subscribe( | ||
createOperatorSubscriber( | ||
subscriber, | ||
(value: Action) => { | ||
lastValue = value; | ||
lastTime = scheduler.now(); | ||
if (!activeTask) { | ||
activeTask = scheduler.schedule(emitWhenIdle, dueTime); | ||
subscriber.add(activeTask); | ||
} else { | ||
// All this code just to place this code block. | ||
const conclude = axiumConclude(); | ||
subscriber.next( | ||
{ | ||
...value, | ||
...conclude, | ||
} | ||
); | ||
} | ||
}, | ||
() => { | ||
emit(); | ||
subscriber.complete(); | ||
}, | ||
undefined, | ||
() => { | ||
lastValue = activeTask = null; | ||
} | ||
) | ||
); | ||
}); | ||
} |
Oops, something went wrong.