UNPKG

@graphql-tools/load

Version:

A set of utils for faster development of GraphQL tools

109 lines (108 loc) • 3.4 kB
function isThenable(value) { return value != null && typeof value.then === 'function'; } export function useQueue(options) { const tasks = []; const concurrency = options?.concurrency; return { add(fn) { tasks.push(fn); }, runAll() { const total = tasks.length; if (total === 0) { return; } const limit = concurrency ?? total; let index = 0; let activeCount = 0; let settledCount = 0; let failed = false; let resolveDone; let rejectDone; let donePromise; const ensureDonePromise = () => { if (!donePromise) { ({ promise: donePromise, resolve: resolveDone, reject: rejectDone, } = Promise.withResolvers()); } return donePromise; }; const onSettle = () => { settledCount++; if (failed) { return; } if (settledCount === total) { resolveDone?.(); return; } pump(); }; const pump = () => { while (!failed && activeCount < limit && index < total) { const task = tasks[index++]; activeCount++; let result; try { result = task(); } catch (error) { failed = true; activeCount--; // If callers are already awaiting the completion gate, reject it. // Otherwise keep the sync failure synchronous. if (rejectDone) { rejectDone(error); return; } throw error; } if (isThenable(result)) { // Only async work needs a completion gate; sync results stay sync. ensureDonePromise(); result.then(() => { activeCount--; onSettle(); }, error => { failed = true; activeCount--; rejectDone(error); }); } else { activeCount--; settledCount++; } } if (!failed && settledCount === total) { resolveDone?.(); } }; pump(); if (failed) { return donePromise; } if (settledCount === total) { return; } return ensureDonePromise(); }, }; } export function useSyncQueue() { const queue = []; return { add(fn) { queue.push(fn); }, runAll() { for (const fn of queue) { fn(); } }, }; }