@rx-angular/cdk
Version:
@rx-angular/cdk is a Component Development Kit for ergonomic and highly performant angular applications. It helps to to build Large scale applications, UI libs, state management, rendering systems and much more. Furthermore the unique way of mixing reacti
205 lines (200 loc) • 7.51 kB
JavaScript
import { Observable, Subscription } from 'rxjs';
const coalescingManager = createCoalesceManager();
function hasKey(ctx, property) {
return ctx[property] != null;
}
/*
* createPropertiesWeakMap
*
* @param getDefaults: (o: O) => P
* Example:
*
* export interface Properties {
* isCoalescing: boolean;
* }
*
* const obj: object = {
* foo: 'bar',
* isCoalescing: 'weakMap version'
* };
*
* const getDefaults = (ctx: object): Properties => ({isCoalescing: false});
* const propsMap = createPropertiesWeakMap<object, Properties>(getDefaults);
*
* console.log('obj before:', obj);
* // {foo: "bar", isCoalescing: "weakMap version"}
* console.log('props before:', propsMap.getProps(obj));
* // {isCoalescing: "weakMap version"}
*
* propsMap.setProps(obj, {isCoalescing: true});
* console.log('obj after:', obj);
* // {foo: "bar", isCoalescing: "weakMap version"}
* console.log('props after:', propsMap.getProps(obj));
* // {isCoalescing: "true"}
* */
function createPropertiesWeakMap(getDefaults) {
const propertyMap = new WeakMap();
return {
getProps: getProperties,
setProps: setProperties,
};
function getProperties(ctx) {
const defaults = getDefaults(ctx);
const propertiesPresent = propertyMap.get(ctx);
let properties;
if (propertiesPresent !== undefined) {
properties = propertiesPresent;
}
else {
properties = {};
Object.entries(defaults).forEach(([prop, value]) => {
if (hasKey(ctx, prop)) {
properties[prop] = ctx[prop];
}
else {
properties[prop] = value;
}
});
propertyMap.set(ctx, properties);
}
return properties;
}
function setProperties(ctx, props) {
const properties = getProperties(ctx);
Object.entries(props).forEach(([prop, value]) => {
properties[prop] = value;
});
propertyMap.set(ctx, properties);
return properties;
}
}
const coalescingContextPropertiesMap = createPropertiesWeakMap((ctx) => ({
numCoalescingSubscribers: 0,
}));
/**
* @describe createCoalesceManager
*
* returns a
* Maintains a weak map of component references ans flags
* them if the coalescing process is already started for them.
*
* Used in render aware internally.
*/
function createCoalesceManager() {
return {
remove: removeWork,
add: addWork,
isCoalescing,
};
// Increments the number of subscriptions in a scope e.g. a class instance
function removeWork(scope) {
const numCoalescingSubscribers = coalescingContextPropertiesMap.getProps(scope).numCoalescingSubscribers -
1;
coalescingContextPropertiesMap.setProps(scope, {
numCoalescingSubscribers: numCoalescingSubscribers >= 0 ? numCoalescingSubscribers : 0,
});
}
// Decrements the number of subscriptions in a scope e.g. a class instance
function addWork(scope) {
const numCoalescingSubscribers = coalescingContextPropertiesMap.getProps(scope).numCoalescingSubscribers +
1;
coalescingContextPropertiesMap.setProps(scope, {
numCoalescingSubscribers,
});
}
// Checks if anybody else is already coalescing atm
function isCoalescing(scope) {
return (coalescingContextPropertiesMap.getProps(scope).numCoalescingSubscribers >
0);
}
}
/**
* @description
* Limits the number of synchronous emitted a value from the source Observable to
* one emitted value per
* durationSelector e.g. [`AnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame), then repeats
* this process for every tick of the browsers event loop.
*
* The coalesce operator is based on the [throttle](https://rxjs-dev.firebaseapp.com/api/operators/throttle) operator.
* In addition to that is provides emitted values for the trailing end only, as well as maintaining a context to scope
* coalescing.
*
* @param {function(value: T): Observable} durationSelector - A function
* that receives a value from the source Observable, for computing the silencing
* duration for each source value, returned as an Observable or a Promise.
* It defaults to `requestAnimationFrame` as durationSelector.
* @param scope
* Defaults to `{ leading: false, trailing: true }`. The default scoping is per subscriber.
* @return {Observable<T>} An Observable that performs the coalesce operation to
* limit the rate of emissions from the source.
*
* @usageNotes
* Emit clicks at a rate of at most one click per second
* ```typescript
* import { interval, fromEvent } from 'rxjs';
* import { coalesceWith } from '@rx-angular/cdk/coalescing';
*
* const setTimeoutDurationSelector = interval(500);
* const clicks = fromEvent(document, 'click');
* const result = clicks.pipe(coalesceWith(setTimeoutDurationSelector));
* result.subscribe(x => console.log(x));
* ```
*/
function coalesceWith(durationSelector, scope) {
const _scope = scope || {};
return (source) => {
return new Observable((observer) => {
const rootSubscription = new Subscription();
rootSubscription.add(source.subscribe(createInnerObserver(observer, rootSubscription)));
return rootSubscription;
});
function createInnerObserver(outerObserver, rootSubscription) {
let actionSubscription;
let latestValue;
const tryEmitLatestValue = () => {
if (actionSubscription) {
// We only decrement the number if it is greater than 0 (isCoalescing)
coalescingManager.remove(_scope);
if (!coalescingManager.isCoalescing(_scope)) {
outerObserver.next(latestValue);
}
}
};
return {
complete: () => {
tryEmitLatestValue();
outerObserver.complete();
},
error: (error) => outerObserver.error(error),
next: (value) => {
latestValue = value;
if (!actionSubscription) {
coalescingManager.add(_scope);
actionSubscription = durationSelector.subscribe({
error: (error) => outerObserver.error(error),
next: () => {
tryEmitLatestValue();
actionSubscription?.unsubscribe();
actionSubscription = undefined;
},
complete: () => {
tryEmitLatestValue();
actionSubscription = undefined;
},
});
rootSubscription.add(new Subscription(() => {
tryEmitLatestValue();
actionSubscription?.unsubscribe();
actionSubscription = undefined;
}));
}
},
};
}
};
}
/**
* Generated bundle index. Do not edit.
*/
export { coalesceWith, coalescingManager };
//# sourceMappingURL=rx-angular-cdk-coalescing.mjs.map