/* * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ export type FetchFunction = (key: K) => Promise; type ResolveReject = Parameters>[0]>; type ResolverPair = { resolve: ResolveReject[0]; reject: ResolveReject[1]; }; export class DebounceLoader { private resolverMap = new Map>(); private promiseMap = new Map>(); private resolvedPromise = Promise.resolve(); constructor(private loadFn: FetchFunction) {} public load(key: K): Promise { const promise = this.promiseMap.get(key); if (typeof promise !== 'undefined') { return promise; } const isFirst = this.promiseMap.size === 0; const newPromise = new Promise((resolve, reject) => { this.resolverMap.set(key, { resolve, reject }); }); this.promiseMap.set(key, newPromise); if (isFirst) { this.enqueueDebouncedLoadJob(); } return newPromise; } private runDebouncedLoad(): void { const resolvers = [...this.resolverMap]; this.resolverMap.clear(); this.promiseMap.clear(); for (const [key, { resolve, reject }] of resolvers) { this.loadFn(key).then(resolve, reject); } } private enqueueDebouncedLoadJob(): void { this.resolvedPromise.then(() => { process.nextTick(() => { this.runDebouncedLoad(); }); }); } }