scrivito
Version:
Scrivito is a professional, yet easy to use SaaS Enterprise Content Management Service, built for digital agencies and medium to large businesses. It is completely maintenance-free, cost-effective, and has unprecedented performance and security.
59 lines (51 loc) • 2.04 kB
text/typescript
import { ContinueIterable, sliceFromIterable } from 'scrivito_sdk/common';
import { loadSerial } from 'scrivito_sdk/loadable/load_serial';
import { loadableFunction } from 'scrivito_sdk/loadable/loadable_function';
/** generate a function that applies the given mapper and reducer over the given input.
*
* the map-reduce is performed in small batches, in order to avoid blocking
* the event loop with long-running events.
*
* The batches are loaded serially (one after the other), in order to avoid flooding
* the event loop with too many small events at once.
* */
export function loadableMapReduce<InputElement, Continuation, ReductionType>(
input: ContinueIterable<InputElement, Continuation>,
mapper: (value: InputElement) => ReductionType,
reducer: (acc: ReductionType, value: ReductionType) => ReductionType,
empty: ReductionType,
batchSize = 20
): () => ReductionType {
/** perform the reduction from the given batch to the last one */
function computeReductionFrom(batchNumber: number): ReductionType {
return loadSerial(
() => getReducedBatch(batchNumber),
({ value, continuation }) =>
!continuation
? value
: reducer(value, computeReductionFrom(batchNumber + 1))
);
}
interface ReducedBatch {
value: ReductionType;
continuation?: Continuation | undefined;
}
type GetReducedBatch = (batchNumber: number) => ReducedBatch;
/** compute the map-reduce for the given batch */
const getReducedBatch: GetReducedBatch = loadableFunction(
{ value: empty },
(batchNumber) => batchNumber.toString(),
(batchNumber: number): ReducedBatch => {
const continueFrom =
batchNumber === 0
? undefined
: getReducedBatch(batchNumber - 1).continuation;
const slice = sliceFromIterable(input, continueFrom, batchSize);
return {
value: slice.values.map(mapper).reduce(reducer, empty),
continuation: slice.continuation,
};
}
);
return () => computeReductionFrom(0);
}