Function batch

Source
batch<TFn extends AnyFn>(
    args: BatchConstructor<TFn>,
): (...args: Parameters<TFn>) => Promise<AwaitRet<TFn>>

An async event wrapper that allows multiple calls in an interval to be batched together.

The underlying function will only be called once per batch. Optionally, the output can be unbatched to match the inputs.

The API is a lot like debounce, but with an additional batch function and an optional unbatch function.

import { batch } from "@pistonite/pure/sync";

const execute = batch({
fn: (n: number) => {
console.log(n);
},
interval: 100,
// batch receives all the inputs and returns a single input
// here we just sums the inputs
batch: (args: [number][]): [number] => [args.reduce((acc, [n]) => acc + n, 0)],
});

await execute(1); // logs 1 immediately
const p1 = execute(2); // will be resolved at 100ms
const p2 = execute(3); // will be resolved at 100ms
await Promise.all([p1, p2]); // logs 5 after 100ms

The optional unbatch function allows the output to be unbatched, so the promises are resolved as if the underlying function is called directly.

Note that unbatching is usually slow and not required.

import { batch } from "@pistonite/pure/sync";

type Message = {
id: number;
payload: string;
}

const execute = batch({
fn: (messages: Message[]): Message[] => {
console.log(messages.length);
return messages.map((m) => ({
id: m.id,
payload: m.payload + "out",
}));
},
batch: (args: [Message[]][]): [Message[]] => {
const out: Message[] = [];
for (const [messages] of args) {
out.push(...messages);
}
return [out];
},
unbatch: (inputs: [Message[]][], output: Message[]): Message[][] => {
// not efficient, but just for demonstration
const idToOutput = new Map();
for (const o of output) {
idToOutput.set(o.id, o);
}
return inputs.map(([messages]) => {
return messages.map(({id}) => {
return idToOutput.get(m.id)!;
});
});
},
interval: 100,
});

const r1 = await execute([{id: 1, payload: "a"}]); // logs 1 immediately
// r1 is [ {id: 1, payload: "aout"} ]

const p1 = execute([{id: 2, payload: "b"}]); // will be resolved at 100ms
const p2 = execute([{id: 3, payload: "c"}]); // will be resolved at 100ms

const r2 = await p2; // 2 is logged
// r1 is [ {id: 2, payload: "bout"} ]
const r3 = await p3; // nothing is logged, as it's already resolved
// r2 is [ {id: 3, payload: "cout"} ]

See BatchConstructor for options