spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
98 lines (94 loc) • 2.63 kB
text/typescript
import { now } from './chrono';
import { causeAsyncException } from './exception';
export function throttle<T, C = unknown>(
interval: number,
callback: (this: C, last: T, buffer: T[]) => Promise<unknown> | unknown,
capacity: number = 1,
): (this: C, arg: T) => void {
// Bug: Karma and TypeScript
let timer: ReturnType<typeof setTimeout> | 0 = 0;
let buffer: T[] = [];
return function self(this: C, data: T) {
if (capacity === 1) {
buffer = [data];
}
else {
buffer.length === capacity && buffer.shift();
buffer.push(data);
}
if (timer !== 0) return;
timer = setTimeout(async () => {
assert(timer !== 0);
assert(buffer.length > 0);
const buf = buffer;
buffer = [];
assert(buf.length > 0);
try {
await callback.call(this, buf[buf.length - 1], buf);
}
catch (reason) {
causeAsyncException(reason);
}
timer = 0;
buffer.length > 0 && self.call(this, buffer.pop()!);
}, interval);
};
}
export function debounce<T, C = unknown>(
delay: number,
callback: (this: C, last: T, buffer: T[]) => Promise<unknown> | unknown,
capacity: number = 1,
): (this: C, arg: T) => void {
// Bug: Karma and TypeScript
let timer: ReturnType<typeof setTimeout> | 0 = 0;
let buffer: T[] = [];
let callable = true;
return function self(this: C, data: T) {
if (capacity === 1) {
buffer = [data];
}
else {
buffer.length === capacity && buffer.shift();
buffer.push(data);
}
if (timer !== 0) return;
timer = setTimeout(() => {
assert(timer !== 0);
assert(buffer.length > 0);
timer = 0;
setTimeout(async () => {
if (timer !== 0) return;
if (!callable) return;
assert(buffer.length > 0);
const buf = buffer;
buffer = [];
assert(buf.length > 0);
callable = false;
try {
await callback.call(this, buf[buf.length - 1], buf);
}
catch (reason) {
causeAsyncException(reason);
}
callable = true;
assert(timer === 0);
buffer.length > 0 && self.call(this, buffer.pop()!);
}, delay);
}, delay);
};
}
export function cothrottle<T>(
routine: () => AsyncGenerator<Awaited<T>>,
resource: number,
scheduler: () => PromiseLike<unknown>,
): () => AsyncGenerator<Awaited<T>> {
return async function* () {
let start = now();
for await (const value of routine()) {
yield value;
if (resource - (now() - start) > 0) continue;
await scheduler();
start = now();
}
};
}