@upendra.manike/async-utils
Version:
Async control utilities for JavaScript/TypeScript: retry with exponential backoff, concurrency-limited queues, promise pools, sequential execution, timeout handling, abort signals. Perfect for API calls, data fetching, and async operations. AI-friendly an
1 lines • 10.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n const t = setTimeout(() => {\n cleanup();\n resolve();\n }, ms);\n const onAbort = () => {\n cleanup();\n reject(new DOMException('Aborted', 'AbortError'));\n };\n const cleanup = () => {\n clearTimeout(t);\n signal?.removeEventListener('abort', onAbort);\n };\n if (signal) {\n if (signal.aborted) return onAbort();\n signal.addEventListener('abort', onAbort);\n }\n });\n}\n\nexport type BackoffKind = 'none' | 'linear' | 'exponential';\n\nexport function backoffDelay(attempt: number, base = 250, kind: BackoffKind = 'exponential', jitter = true): number {\n let delay = base;\n if (kind === 'linear') delay = base * attempt;\n if (kind === 'exponential') delay = base * Math.pow(2, attempt - 1);\n if (jitter) delay = Math.random() * delay;\n return Math.min(delay, 30_000);\n}\n\nexport interface RetryOptions {\n retries?: number;\n baseDelayMs?: number;\n backoff?: BackoffKind;\n retryable?: (error: unknown) => boolean;\n signal?: AbortSignal;\n}\n\nexport async function withRetry<T>(fn: () => Promise<T>, opts: RetryOptions = {}): Promise<T> {\n const {\n retries = 3,\n baseDelayMs = 250,\n backoff = 'exponential',\n retryable = () => true,\n signal,\n } = opts;\n\n let attempt = 0;\n // eslint-disable-next-line no-constant-condition\n while (true) {\n attempt += 1;\n try {\n if (signal?.aborted) throw new DOMException('Aborted', 'AbortError');\n return await fn();\n } catch (err) {\n if (attempt > retries || !retryable(err)) throw err;\n const delay = backoffDelay(attempt, baseDelayMs, backoff);\n await sleep(delay, signal);\n }\n }\n}\n\nexport async function pMapSeries<I, O>(items: readonly I[], mapper: (item: I, index: number) => Promise<O>): Promise<O[]> {\n const out: O[] = [];\n for (let i = 0; i < items.length; i += 1) {\n // eslint-disable-next-line no-await-in-loop\n out.push(await mapper(items[i], i));\n }\n return out;\n}\n\nexport function timeoutPromise<T>(promise: Promise<T>, ms: number, message = 'Timed out'): Promise<T> {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(message)), ms);\n });\n return Promise.race([promise, timeout]).finally(() => clearTimeout(timer!)) as Promise<T>;\n}\n\nexport async function pQueue<T>(tasks: Array<() => Promise<T>>): Promise<T[]> {\n const results: T[] = [];\n for (const task of tasks) {\n // eslint-disable-next-line no-await-in-loop\n results.push(await task());\n }\n return results;\n}\n\nexport async function pMapLimit<I, O>(\n items: readonly I[],\n limit: number,\n mapper: (item: I, index: number) => Promise<O>\n): Promise<O[]> {\n if (limit <= 0) return [];\n const results: O[] = new Array(items.length);\n let nextIndex = 0;\n const workers = new Array(Math.min(limit, items.length)).fill(0).map(async () => {\n while (true) {\n const current = nextIndex;\n if (current >= items.length) break;\n nextIndex += 1;\n // eslint-disable-next-line no-await-in-loop\n results[current] = await mapper(items[current], current);\n }\n });\n await Promise.all(workers);\n return results;\n}\n\nexport function memoizeAsync<A extends any[], R>(\n fn: (...args: A) => Promise<R>,\n keyFn?: (...args: A) => string\n): (...args: A) => Promise<R> {\n const cache = new Map<string, Promise<R>>();\n return (...args: A) => {\n const key = keyFn ? keyFn(...args) : JSON.stringify(args);\n let cached = cache.get(key);\n if (!cached) {\n cached = fn(...args);\n cache.set(key, cached);\n }\n return cached;\n };\n}\n\nexport function promiseRace<T>(promises: Iterable<Promise<T>>): Promise<T> {\n return Promise.race(promises);\n}\n\n/**\n * Concurrency-limited task queue\n */\nexport async function concurrencyLimitedQueue<T>(\n tasks: Array<() => Promise<T>>,\n concurrency: number\n): Promise<T[]> {\n if (concurrency <= 0) return [];\n const results: T[] = new Array(tasks.length);\n let next = 0;\n let running = 0;\n let resolveAll: (() => void) | null = null;\n const done = new Promise<void>((resolve) => (resolveAll = resolve));\n\n return new Promise<T[]>((resolve, reject) => {\n const runNext = () => {\n if (next >= tasks.length && running === 0) {\n resolveAll!();\n return resolve(results);\n }\n while (running < concurrency && next < tasks.length) {\n const current = next++;\n running += 1;\n tasks[current]()\n .then((val) => {\n results[current] = val;\n })\n .catch(reject)\n .finally(() => {\n running -= 1;\n runNext();\n });\n }\n };\n runNext();\n });\n}\n\n/**\n * Promise pool executor (accepts generator of tasks)\n */\nexport async function promisePool<T>(\n iterator: Iterable<() => Promise<T>>,\n concurrency: number\n): Promise<T[]> {\n const tasks = Array.from(iterator);\n return concurrencyLimitedQueue(tasks, concurrency);\n}\n\nexport const asyncUtils = { sleep, backoffDelay, withRetry, pMapSeries, timeoutPromise, pQueue, pMapLimit, memoizeAsync, promiseRace, concurrencyLimitedQueue, promisePool };\n\n\n"]}