UNPKG

storyblok-js-client

Version:
94 lines (76 loc) 1.89 kB
import type { ISbThrottle, Queue } from './interfaces'; class AbortError extends Error { constructor(msg: string) { super(msg); this.name = 'AbortError'; } } function throttledQueue<T extends (...args: Parameters<T>) => ReturnType<T>>( fn: T, limit: number, interval: number, ): ISbThrottle<T> { if (!Number.isFinite(limit)) { throw new TypeError('Expected `limit` to be a finite number'); } if (!Number.isFinite(interval)) { throw new TypeError('Expected `interval` to be a finite number'); } const queue: Queue<Parameters<T>>[] = []; let timeouts: ReturnType<typeof setTimeout>[] = []; let activeCount = 0; let isAborted = false; const next = async () => { activeCount++; const x = queue.shift(); if (x) { try { const res = await fn(...x.args); x.resolve(res); } catch (error) { x.reject(error); } } const id = setTimeout(() => { activeCount--; if (queue.length > 0) { next(); } timeouts = timeouts.filter(currentId => currentId !== id); }, interval); if (!timeouts.includes(id)) { timeouts.push(id); } }; const throttled: ISbThrottle<T> = (...args) => { if (isAborted) { return Promise.reject( new Error( 'Throttled function is already aborted and not accepting new promises', ), ); } return new Promise((resolve, reject) => { queue.push({ resolve, reject, args, }); if (activeCount < limit) { next(); } }); }; throttled.abort = () => { isAborted = true; timeouts.forEach(clearTimeout); timeouts = []; queue.forEach(x => x.reject(() => new AbortError('Throttle function aborted')), ); queue.length = 0; }; return throttled; } export default throttledQueue;