packages/runtime/src/subscriber-batch.ts
import {
type ISubscriberRecord,
type ICollectionSubscriber,
type IndexMap,
type Collection,
} from './interfaces';
import type { IAnySubscriber } from './subscriber-collection';
type ValueBatchRecord = [
1,
unknown, // oldValue
unknown, // newValue
];
type CollectionBatchRecord = [
2,
Collection,
IndexMap,
];
type BatchRecord = ValueBatchRecord | CollectionBatchRecord;
type Batch = Map<ISubscriberRecord<IAnySubscriber>, BatchRecord>;
let currBatch: Batch | null = new Map();
// eslint-disable-next-line import/no-mutable-exports
export let batching = false;
export function batch(fn: () => unknown): void {
const prevBatch = currBatch;
const newBatch: Batch = currBatch = new Map();
batching = true;
try {
fn();
} finally {
currBatch = null;
batching = false;
try {
let pair: [ISubscriberRecord<IAnySubscriber>, BatchRecord];
let subs: ISubscriberRecord<IAnySubscriber>;
let batchRecord: BatchRecord;
let col: Collection;
let indexMap: IndexMap;
let hasChanges = false;
let i: number;
let ii: number;
for (pair of newBatch) {
subs = pair[0];
batchRecord = pair[1];
if (prevBatch?.has(subs)) {
prevBatch.set(subs, batchRecord);
}
if (batchRecord[0] === 1) {
subs.notify(batchRecord[1], batchRecord[2]);
} else {
col = batchRecord[1];
indexMap = batchRecord[2];
hasChanges = false;
if (indexMap.deletedIndices.length > 0) {
hasChanges = true;
} else {
for (i = 0, ii = indexMap.length; i < ii; ++i) {
if (indexMap[i] !== i) {
hasChanges = true;
break;
}
}
}
if (hasChanges) {
subs.notifyCollection(col, indexMap);
}
}
}
} finally {
currBatch = prevBatch;
}
}
}
export function addCollectionBatch(
subs: ISubscriberRecord<ICollectionSubscriber>,
collection: Collection,
indexMap: IndexMap,
) {
if (!currBatch!.has(subs)) {
currBatch!.set(subs, [2, collection, indexMap]);
} else {
currBatch!.get(subs)![2] = indexMap;
}
}
export function addValueBatch(
subs: ISubscriberRecord<IAnySubscriber>,
newValue: unknown,
oldValue: unknown,
) {
const batchRecord = currBatch!.get(subs);
if (batchRecord === void 0) {
currBatch!.set(subs, [1, newValue, oldValue]);
} else {
batchRecord[1] = newValue;
batchRecord[2] = oldValue;
}
}